blob: 943928ed850bf8b1141f35caede5e514c91b4e64 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SRC_DEVELOPER_FORENSICS_TESTING_GMATCHERS_H_
#define SRC_DEVELOPER_FORENSICS_TESTING_GMATCHERS_H_
#include <fuchsia/feedback/cpp/fidl.h>
#include <sstream>
#include <string>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "src/developer/forensics/feedback/attachments/types.h"
#include "src/developer/forensics/utils/errors.h"
#include "src/lib/fsl/vmo/strings.h"
namespace forensics {
namespace internal {
// Allows expectations to be set on AttachmentValue. Needed because values can't easily be compared
// for equality due to the move-only nature of AttachmentValue.
class AttachmentValueMatcher
: public ::testing::MatcherInterface<const feedback::AttachmentValue&> {
public:
using is_gtest_matcher = void;
AttachmentValueMatcher(std::string value) : value_(std::move(value)), error_(std::nullopt) {}
AttachmentValueMatcher(const Error error) : value_(std::nullopt), error_(error) {}
AttachmentValueMatcher(std::string value, const Error error)
: value_(std::move(value)), error_(error) {}
bool MatchAndExplain(const feedback::AttachmentValue& val,
::testing::MatchResultListener* listener) const override {
const bool value_matches =
val.HasValue() == value_.has_value() && (!val.HasValue() || val.Value() == *value_);
const bool error_matches =
val.HasError() == error_.has_value() && (!val.HasError() || val.Error() == *error_);
if (value_matches && error_matches) {
return true;
}
*listener << "expected attachment value " << Describe();
return false;
}
void DescribeTo(::std::ostream* os) const override { *os << Describe(); }
void DescribeNegationTo(::std::ostream* os) const override { *os << " not " << Describe(); }
private:
std::string Describe() const {
std::stringstream ss;
if (value_.has_value()) {
ss << "has a value \"" << *value_ << "\"";
} else {
ss << "does not have a value";
}
ss << " and ";
if (error_.has_value()) {
ss << "has an error \"" << ToString(*error_) << "\"";
} else {
ss << "does not have an error";
}
return ss.str();
}
std::optional<std::string> value_;
std::optional<Error> error_;
};
// Compares two Attachment objects.
template <typename ResultListenerT>
bool DoAttachmentMatch(const fuchsia::feedback::Attachment& actual, const std::string& expected_key,
const std::string& expected_value, ResultListenerT* result_listener) {
if (actual.key != expected_key) {
*result_listener << "Expected key " << expected_key << ", got " << actual.key;
return false;
}
std::string actual_value;
if (!fsl::StringFromVmo(actual.value, &actual_value)) {
*result_listener << "Cannot parse actual VMO for key " << actual.key << " to string";
return false;
}
if (actual_value != expected_value) {
*result_listener << "Expected value " << expected_value << ", got " << actual_value;
return false;
}
return true;
}
// Compares two Annotation objects.
template <typename ResultListenerT>
bool DoAnnotationMatch(const fuchsia::feedback::Annotation& actual, const std::string& expected_key,
const std::string& expected_value, ResultListenerT* result_listener) {
if (actual.key != expected_key) {
*result_listener << "Expected key " << expected_key << ", got " << actual.key;
return false;
}
if (actual.value != expected_value) {
*result_listener << "Expected value " << expected_value << ", got " << actual.value;
return false;
}
return true;
}
template <typename ResultListenerT>
bool DoStringBufferMatch(const fuchsia::mem::Buffer& actual, const std::string& expected,
ResultListenerT* result_listener) {
std::string actual_value;
if (!fsl::StringFromVmo(actual, &actual_value)) {
*result_listener << "Cannot parse actual VMO to string";
return false;
}
if (actual_value != expected) {
return false;
}
return true;
}
} // namespace internal
// Returns true if gMock |arg|.key matches |expected_key|.
MATCHER_P(MatchesKey, expected_key,
"matches an element with key '" + std::string(expected_key) + "'") {
return arg.key == expected_key;
}
// Returns true if gMock |arg|.key matches |expected_key| and str(|arg|.value) matches
// |expected_value|, assuming two Attachment objects.
MATCHER_P2(MatchesAttachment, expected_key, expected_value,
"matches an attachment with key '" + std::string(expected_key) + "' and value '" +
std::string(expected_value) + "'") {
return internal::DoAttachmentMatch(arg, expected_key, expected_value, result_listener);
}
// Returns true if gMock |arg|.key matches |expected_key| and str(|arg|.value) matches
// |expected_value|, assuming two Annotation objects.
MATCHER_P2(MatchesAnnotation, expected_key, expected_value,
"matches an annotation with key '" + std::string(expected_key) + "' and value '" +
std::string(expected_value) + "'") {
return internal::DoAnnotationMatch(arg, expected_key, expected_value, result_listener);
}
// Returns true if gMock str(|arg|) matches |expected|.
MATCHER_P(MatchesStringBuffer, expected, "'" + std::string(expected) + "'") {
return internal::DoStringBufferMatch(arg, expected, result_listener);
}
MATCHER(HasValue, negation ? "is error" : "has value") {
if (arg.HasValue()) {
return true;
}
return false;
}
inline testing::Matcher<feedback::AttachmentValue> AttachmentValueIs(std::string data) {
return internal::AttachmentValueMatcher(std::move(data));
}
inline testing::Matcher<feedback::AttachmentValue> AttachmentValueIs(const Error error) {
return internal::AttachmentValueMatcher(error);
}
inline testing::Matcher<feedback::AttachmentValue> AttachmentValueIs(std::string data,
const Error error) {
return internal::AttachmentValueMatcher(std::move(data), error);
}
} // namespace forensics
#endif // SRC_DEVELOPER_FORENSICS_TESTING_GMATCHERS_H_