Result: Support composite results (#775)
This change adds the `+=` operator to `amber::Result`, allowing results to contain more than one error message.
This simplifies the accumulation of errors produced by concurrent testing.
Also make `Result::Error()` return `"<no error message given>"` if a `Result` is constructed with an empty string.
Added tests.
diff --git a/include/amber/result.h b/include/amber/result.h
index 6e916be..c85c281 100644
--- a/include/amber/result.h
+++ b/include/amber/result.h
@@ -24,23 +24,30 @@
class Result {
public:
/// Creates a result which succeeded.
- Result();
+ Result() = default;
+
/// Creates a result which failed and will return |err|.
explicit Result(const std::string& err);
- Result(const Result&);
- ~Result();
+ inline Result(const Result&) = default;
+ inline Result(Result&&) = default;
- Result& operator=(const Result&);
+ inline Result& operator=(const Result&) = default;
+ inline Result& operator=(Result&&) = default;
+
+ /// Adds the errors from |res| to this Result.
+ Result& operator+=(const Result& res);
+
+ /// Adds the error |err| to this Result.
+ Result& operator+=(const std::string& err);
/// Returns true if the result is a success.
- bool IsSuccess() const { return succeeded_; }
+ bool IsSuccess() const { return errors_.size() == 0; }
/// Returns the error string if |IsSuccess| is false.
- const std::string& Error() const { return error_; }
+ std::string Error() const;
private:
- bool succeeded_;
- std::string error_;
+ std::vector<std::string> errors_;
};
} // namespace amber
diff --git a/src/result.cc b/src/result.cc
index 39b3a85..160070d 100644
--- a/src/result.cc
+++ b/src/result.cc
@@ -14,16 +14,44 @@
#include "amber/result.h"
+#include <sstream>
+
namespace amber {
-Result::Result() : succeeded_(true) {}
+Result::Result(const std::string& err) {
+ errors_.emplace_back(err);
+}
-Result::Result(const std::string& err) : succeeded_(false), error_(err) {}
+Result& Result::operator+=(const Result& res) {
+ errors_.insert(std::end(errors_), std::begin(res.errors_),
+ std::end(res.errors_));
+ return *this;
+}
-Result::Result(const Result&) = default;
+Result& Result::operator+=(const std::string& err) {
+ errors_.emplace_back(err);
+ return *this;
+}
-Result::~Result() = default;
-
-Result& Result::operator=(const Result&) = default;
+std::string Result::Error() const {
+ static const char* kNoErrorMsg = "<no error message given>";
+ switch (errors_.size()) {
+ case 0:
+ return "";
+ case 1:
+ return errors_[0].size() > 0 ? errors_[0] : kNoErrorMsg;
+ default: {
+ std::stringstream ss;
+ ss << errors_.size() << " errors:";
+ for (size_t i = 0; i < errors_.size(); i++) {
+ auto& err = errors_[i];
+ ss << "\n";
+ ss << " (" << (i + 1) << ") ";
+ ss << (err.size() > 0 ? err : kNoErrorMsg);
+ }
+ return ss.str();
+ }
+ }
+}
} // namespace amber
diff --git a/src/result_test.cc b/src/result_test.cc
index 7f850f9..de74837 100644
--- a/src/result_test.cc
+++ b/src/result_test.cc
@@ -31,6 +31,12 @@
EXPECT_EQ("Test Failed", r.Error());
}
+TEST_F(ResultTest, ErrorWithEmptyString) {
+ Result r("");
+ EXPECT_FALSE(r.IsSuccess());
+ EXPECT_EQ("<no error message given>", r.Error());
+}
+
TEST_F(ResultTest, Copy) {
Result r("Testing");
Result r2(r);
@@ -39,4 +45,71 @@
EXPECT_EQ("Testing", r2.Error());
}
+TEST_F(ResultTest, Append1String) {
+ Result r;
+ r += "Test Failed";
+ EXPECT_EQ("Test Failed", r.Error());
+}
+
+TEST_F(ResultTest, Append3Strings) {
+ Result r;
+ r += "Error one";
+ r += "Error two";
+ r += "Error three";
+ EXPECT_EQ(R"(3 errors:
+ (1) Error one
+ (2) Error two
+ (3) Error three)",
+ r.Error());
+}
+
+TEST_F(ResultTest, Append1SingleErrorResult) {
+ Result r;
+ r += Result("Test Failed");
+ EXPECT_EQ("Test Failed", r.Error());
+}
+
+TEST_F(ResultTest, Append3SingleErrorResults) {
+ Result r;
+ r += Result("Error one");
+ r += Result("Error two");
+ r += Result("Error three");
+ EXPECT_EQ(R"(3 errors:
+ (1) Error one
+ (2) Error two
+ (3) Error three)",
+ r.Error());
+}
+
+TEST_F(ResultTest, Append3MixedResults) {
+ Result r;
+ r += Result("Error one");
+ r += Result(); // success
+ r += Result("Error two");
+ EXPECT_EQ(R"(2 errors:
+ (1) Error one
+ (2) Error two)",
+ r.Error());
+}
+
+TEST_F(ResultTest, AppendMultipleErrorResults) {
+ Result r1;
+ r1 += Result("r1 error one");
+ r1 += Result("r1 error two");
+ r1 += Result("r1 error three");
+ r1 += Result("");
+ Result r2;
+ r2 += Result("r2 error one");
+ r2 += r1;
+ r2 += Result("r2 error two");
+ EXPECT_EQ(R"(6 errors:
+ (1) r2 error one
+ (2) r1 error one
+ (3) r1 error two
+ (4) r1 error three
+ (5) <no error message given>
+ (6) r2 error two)",
+ r2.Error());
+}
+
} // namespace amber