Report failing manifold operations from the manifold matchers

The matchers assert operation results using EXPECT_TRUE, a side effect
that fails tests even through negated matchers. Fail the match and
explain the failing operation instead.

Change-Id: I10f490ad1287f2582816ecdc546c1555ab7ed650
diff --git a/include/ceres/manifold_test_utils.h b/include/ceres/manifold_test_utils.h
index 765b27a..be7f0b9 100644
--- a/include/ceres/manifold_test_utils.h
+++ b/include/ceres/manifold_test_utils.h
@@ -105,7 +105,10 @@
 
   Vector actual = Vector::Zero(ambient_size);
   Vector zero = Vector::Zero(tangent_size);
-  EXPECT_TRUE(arg.Plus(x.data(), zero.data(), actual.data()));
+  if (!arg.Plus(x.data(), zero.data(), actual.data())) {
+    *result_listener << "\nPlus() returned false";
+    return false;
+  }
   const double n = (actual - Vector{x}).norm();
   const double d = x.norm();
   const double diffnorm = (d == 0.0) ? n : (n / d);
@@ -122,7 +125,10 @@
 MATCHER_P2(XMinusXIsZeroAt, x, tolerance, "") {
   const int tangent_size = arg.TangentSize();
   Vector actual = Vector::Zero(tangent_size);
-  EXPECT_TRUE(arg.Minus(x.data(), x.data(), actual.data()));
+  if (!arg.Minus(x.data(), x.data(), actual.data())) {
+    *result_listener << "\nMinus() returned false";
+    return false;
+  }
   const double diffnorm = actual.norm();
   if (diffnorm > tolerance) {
     *result_listener << "\nx: " << x.transpose()  //
@@ -168,11 +174,16 @@
   Matrix expected = Matrix::Zero(ambient_size, tangent_size);
   double* jacobians[1] = {expected.data()};
 
-  EXPECT_TRUE(
-      cost_function.Evaluate(parameters, x_plus_zero.data(), jacobians));
+  if (!cost_function.Evaluate(parameters, x_plus_zero.data(), jacobians)) {
+    *result_listener << "\ncost_function.Evaluate() returned false";
+    return false;
+  }
 
   Matrix actual = Matrix::Random(ambient_size, tangent_size);
-  EXPECT_TRUE(arg.PlusJacobian(x.data(), actual.data()));
+  if (!arg.PlusJacobian(x.data(), actual.data())) {
+    *result_listener << "\nPlusJacobian() returned false";
+    return false;
+  }
 
   const double n = (actual - expected).norm();
   const double d = expected.norm();
@@ -192,9 +203,15 @@
   const int ambient_size = arg.AmbientSize();
   const int tangent_size = arg.TangentSize();
   Vector x_plus_delta = Vector::Zero(ambient_size);
-  EXPECT_TRUE(arg.Plus(x.data(), delta.data(), x_plus_delta.data()));
+  if (!arg.Plus(x.data(), delta.data(), x_plus_delta.data())) {
+    *result_listener << "\nPlus() returned false";
+    return false;
+  }
   Vector actual = Vector::Zero(tangent_size);
-  EXPECT_TRUE(arg.Minus(x_plus_delta.data(), x.data(), actual.data()));
+  if (!arg.Minus(x_plus_delta.data(), x.data(), actual.data())) {
+    *result_listener << "\nMinus() returned false";
+    return false;
+  }
 
   const double n = (actual - Vector{delta}).norm();
   const double d = delta.norm();
@@ -216,10 +233,16 @@
   const int tangent_size = arg.TangentSize();
 
   Vector y_minus_x = Vector::Zero(tangent_size);
-  EXPECT_TRUE(arg.Minus(y.data(), x.data(), y_minus_x.data()));
+  if (!arg.Minus(y.data(), x.data(), y_minus_x.data())) {
+    *result_listener << "\nMinus() returned false";
+    return false;
+  }
 
   Vector actual = Vector::Zero(ambient_size);
-  EXPECT_TRUE(arg.Plus(x.data(), y_minus_x.data(), actual.data()));
+  if (!arg.Plus(x.data(), y_minus_x.data(), actual.data())) {
+    *result_listener << "\nPlus() returned false";
+    return false;
+  }
 
   using ManifoldType = std::decay_t<decltype(arg)>;
 
@@ -306,10 +329,16 @@
   Matrix expected = Matrix::Zero(tangent_size, ambient_size);
   double* jacobians[1] = {expected.data()};
 
-  EXPECT_TRUE(cost_function.Evaluate(parameters, y_minus_x.data(), jacobians));
+  if (!cost_function.Evaluate(parameters, y_minus_x.data(), jacobians)) {
+    *result_listener << "\ncost_function.Evaluate() returned false";
+    return false;
+  }
 
   Matrix actual = Matrix::Random(tangent_size, ambient_size);
-  EXPECT_TRUE(arg.MinusJacobian(x.data(), actual.data()));
+  if (!arg.MinusJacobian(x.data(), actual.data())) {
+    *result_listener << "\nMinusJacobian() returned false";
+    return false;
+  }
 
   const double n = (actual - expected).norm();
   const double d = expected.norm();
@@ -331,9 +360,15 @@
   const int tangent_size = arg.TangentSize();
 
   Matrix plus_jacobian(ambient_size, tangent_size);
-  EXPECT_TRUE(arg.PlusJacobian(x.data(), plus_jacobian.data()));
+  if (!arg.PlusJacobian(x.data(), plus_jacobian.data())) {
+    *result_listener << "\nPlusJacobian() returned false";
+    return false;
+  }
   Matrix minus_jacobian(tangent_size, ambient_size);
-  EXPECT_TRUE(arg.MinusJacobian(x.data(), minus_jacobian.data()));
+  if (!arg.MinusJacobian(x.data(), minus_jacobian.data())) {
+    *result_listener << "\nMinusJacobian() returned false";
+    return false;
+  }
 
   const Matrix actual = minus_jacobian * plus_jacobian;
   const Matrix expected = Matrix::Identity(tangent_size, tangent_size);
@@ -362,14 +397,20 @@
   constexpr int kMaxNumRows = 3;
   for (int num_rows = kMinNumRows; num_rows <= kMaxNumRows; ++num_rows) {
     Matrix plus_jacobian = Matrix::Random(ambient_size, tangent_size);
-    EXPECT_TRUE(arg.PlusJacobian(x.data(), plus_jacobian.data()));
+    if (!arg.PlusJacobian(x.data(), plus_jacobian.data())) {
+      *result_listener << "\nPlusJacobian() returned false";
+      return false;
+    }
 
     Matrix ambient_matrix = Matrix::Random(num_rows, ambient_size);
     Matrix expected = ambient_matrix * plus_jacobian;
 
     Matrix actual = Matrix::Random(num_rows, tangent_size);
-    EXPECT_TRUE(arg.RightMultiplyByPlusJacobian(
-        x.data(), num_rows, ambient_matrix.data(), actual.data()));
+    if (!arg.RightMultiplyByPlusJacobian(
+            x.data(), num_rows, ambient_matrix.data(), actual.data())) {
+      *result_listener << "\nRightMultiplyByPlusJacobian() returned false";
+      return false;
+    }
     const double n = (actual - expected).norm();
     const double d = expected.norm();
     const double diffnorm = (d == 0.0) ? n : (n / d);
diff --git a/internal/ceres/manifold_test.cc b/internal/ceres/manifold_test.cc
index 019d648..5a861d6 100644
--- a/internal/ceres/manifold_test.cc
+++ b/internal/ceres/manifold_test.cc
@@ -51,9 +51,47 @@
 
 namespace ceres::internal {
 
+using ::testing::Not;
+
 constexpr int kNumTrials = 1000;
 constexpr double kTolerance = 1e-9;
 
+// A one-dimensional manifold whose operations fail.
+class FailingManifold final : public Manifold {
+ public:
+  int AmbientSize() const final { return 1; }
+  int TangentSize() const final { return 1; }
+  bool Plus(const double* x,
+            const double* delta,
+            double* x_plus_delta) const final {
+    return false;
+  }
+  bool PlusJacobian(const double* x, double* jacobian) const final {
+    return false;
+  }
+  bool Minus(const double* y, const double* x, double* y_minus_x) const final {
+    return false;
+  }
+  bool MinusJacobian(const double* x, double* jacobian) const final {
+    return false;
+  }
+};
+
+TEST(ManifoldTestUtils, MatchersRejectFailingOperations) {
+  const FailingManifold manifold;
+  const Vector x = Vector::Zero(1);
+  const Vector delta = Vector::Zero(1);
+  EXPECT_THAT(manifold, Not(XPlusZeroIsXAt(x, kTolerance)));
+  EXPECT_THAT(manifold, Not(XMinusXIsZeroAt(x, kTolerance)));
+  EXPECT_THAT(manifold, Not(MinusPlusIsIdentityAt(x, delta, kTolerance)));
+  EXPECT_THAT(manifold, Not(PlusMinusIsIdentityAt(x, x, kTolerance)));
+  EXPECT_THAT(manifold, Not(HasCorrectPlusJacobianAt(x, kTolerance)));
+  EXPECT_THAT(manifold, Not(HasCorrectMinusJacobianAt(x, kTolerance)));
+  EXPECT_THAT(manifold, Not(MinusPlusJacobianIsIdentityAt(x, kTolerance)));
+  EXPECT_THAT(manifold,
+              Not(HasCorrectRightMultiplyByPlusJacobianAt(x, kTolerance)));
+}
+
 TEST(EuclideanManifold, StaticNormalFunctionTest) {
   EuclideanManifold<3> manifold;
   EXPECT_EQ(manifold.AmbientSize(), 3);