Googletest export
Introduce a new matcher for unescaping Base-64 strings to gmock. PiperOrigin-RevId: 388471904
This commit is contained in:
parent
c22ce88775
commit
652ec31f9f
@ -88,16 +88,17 @@ The `argument` can be either a C string or a C++ string object:
|
|||||||
|
|
||||||
| Matcher | Description |
|
| Matcher | Description |
|
||||||
| :---------------------- | :------------------------------------------------- |
|
| :---------------------- | :------------------------------------------------- |
|
||||||
| `ContainsRegex(string)` | `argument` matches the given regular expression. |
|
| `ContainsRegex(string)` | `argument` matches the given regular expression. |
|
||||||
| `EndsWith(suffix)` | `argument` ends with string `suffix`. |
|
| `EndsWith(suffix)` | `argument` ends with string `suffix`. |
|
||||||
| `HasSubstr(string)` | `argument` contains `string` as a sub-string. |
|
| `HasSubstr(string)` | `argument` contains `string` as a sub-string. |
|
||||||
| `IsEmpty()` | `argument` is an empty string. |
|
| `IsEmpty()` | `argument` is an empty string. |
|
||||||
| `MatchesRegex(string)` | `argument` matches the given regular expression with the match starting at the first character and ending at the last character. |
|
| `MatchesRegex(string)` | `argument` matches the given regular expression with the match starting at the first character and ending at the last character. |
|
||||||
| `StartsWith(prefix)` | `argument` starts with string `prefix`. |
|
| `StartsWith(prefix)` | `argument` starts with string `prefix`. |
|
||||||
| `StrCaseEq(string)` | `argument` is equal to `string`, ignoring case. |
|
| `StrCaseEq(string)` | `argument` is equal to `string`, ignoring case. |
|
||||||
| `StrCaseNe(string)` | `argument` is not equal to `string`, ignoring case. |
|
| `StrCaseNe(string)` | `argument` is not equal to `string`, ignoring case. |
|
||||||
| `StrEq(string)` | `argument` is equal to `string`. |
|
| `StrEq(string)` | `argument` is equal to `string`. |
|
||||||
| `StrNe(string)` | `argument` is not equal to `string`. |
|
| `StrNe(string)` | `argument` is not equal to `string`. |
|
||||||
|
| `WhenBase64Unescaped(m)` | `argument` is a base-64 escaped string whose unescaped string matches `m`. |
|
||||||
|
|
||||||
`ContainsRegex()` and `MatchesRegex()` take ownership of the `RE` object. They
|
`ContainsRegex()` and `MatchesRegex()` take ownership of the `RE` object. They
|
||||||
use the regular expression syntax defined
|
use the regular expression syntax defined
|
||||||
|
@ -1122,6 +1122,45 @@ class EndsWithMatcher {
|
|||||||
const StringType suffix_;
|
const StringType suffix_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Implements the polymorphic WhenBase64Unescaped(matcher) matcher, which can be
|
||||||
|
// used as a Matcher<T> as long as T can be converted to a string.
|
||||||
|
class WhenBase64UnescapedMatcher {
|
||||||
|
public:
|
||||||
|
using is_gtest_matcher = void;
|
||||||
|
|
||||||
|
explicit WhenBase64UnescapedMatcher(
|
||||||
|
const Matcher<const std::string&>& internal_matcher)
|
||||||
|
: internal_matcher_(internal_matcher) {}
|
||||||
|
|
||||||
|
// Matches anything that can convert to std::string.
|
||||||
|
template <typename MatcheeStringType>
|
||||||
|
bool MatchAndExplain(const MatcheeStringType& s,
|
||||||
|
MatchResultListener* listener) const {
|
||||||
|
const std::string s2(s); // NOLINT (needed for working with string_view).
|
||||||
|
std::string unescaped;
|
||||||
|
if (!internal::Base64Unescape(s2, &unescaped)) {
|
||||||
|
if (listener != nullptr) {
|
||||||
|
*listener << "is not a valid base64 escaped string";
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return MatchPrintAndExplain(unescaped, internal_matcher_, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DescribeTo(::std::ostream* os) const {
|
||||||
|
*os << "matches after Base64Unescape ";
|
||||||
|
internal_matcher_.DescribeTo(os);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DescribeNegationTo(::std::ostream* os) const {
|
||||||
|
*os << "does not match after Base64Unescape ";
|
||||||
|
internal_matcher_.DescribeTo(os);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const Matcher<const std::string&> internal_matcher_;
|
||||||
|
};
|
||||||
|
|
||||||
// Implements a matcher that compares the two fields of a 2-tuple
|
// Implements a matcher that compares the two fields of a 2-tuple
|
||||||
// using one of the ==, <=, <, etc, operators. The two fields being
|
// using one of the ==, <=, <, etc, operators. The two fields being
|
||||||
// compared don't have to have the same type.
|
// compared don't have to have the same type.
|
||||||
@ -4986,6 +5025,14 @@ inline internal::AddressMatcher<InnerMatcher> Address(
|
|||||||
const InnerMatcher& inner_matcher) {
|
const InnerMatcher& inner_matcher) {
|
||||||
return internal::AddressMatcher<InnerMatcher>(inner_matcher);
|
return internal::AddressMatcher<InnerMatcher>(inner_matcher);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Matches a base64 escaped string, when the unescaped string matches the
|
||||||
|
// internal matcher.
|
||||||
|
template <typename MatcherType>
|
||||||
|
internal::WhenBase64UnescapedMatcher WhenBase64Unescaped(
|
||||||
|
const MatcherType& internal_matcher) {
|
||||||
|
return internal::WhenBase64UnescapedMatcher(internal_matcher);
|
||||||
|
}
|
||||||
} // namespace no_adl
|
} // namespace no_adl
|
||||||
|
|
||||||
// Returns a predicate that is satisfied by anything that matches the
|
// Returns a predicate that is satisfied by anything that matches the
|
||||||
|
@ -447,6 +447,8 @@ struct Function<R(Args...)> {
|
|||||||
template <typename R, typename... Args>
|
template <typename R, typename... Args>
|
||||||
constexpr size_t Function<R(Args...)>::ArgumentCount;
|
constexpr size_t Function<R(Args...)>::ArgumentCount;
|
||||||
|
|
||||||
|
bool Base64Unescape(const std::string& encoded, std::string* decoded);
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
# pragma warning(pop)
|
# pragma warning(pop)
|
||||||
#endif
|
#endif
|
||||||
|
@ -37,8 +37,14 @@
|
|||||||
#include "gmock/internal/gmock-internal-utils.h"
|
#include "gmock/internal/gmock-internal-utils.h"
|
||||||
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cctype>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
#include <ostream> // NOLINT
|
#include <ostream> // NOLINT
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "gmock/gmock.h"
|
#include "gmock/gmock.h"
|
||||||
#include "gmock/internal/gmock-port.h"
|
#include "gmock/internal/gmock-port.h"
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
@ -196,5 +202,53 @@ GTEST_API_ void IllegalDoDefault(const char* file, int line) {
|
|||||||
"the variable in various places.");
|
"the variable in various places.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr char UnBase64Impl(char c, const char* const base64, char carry) {
|
||||||
|
return *base64 == 0 ? static_cast<char>(65)
|
||||||
|
: *base64 == c ? carry
|
||||||
|
: UnBase64Impl(c, base64 + 1, carry + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <size_t... I>
|
||||||
|
constexpr std::array<char, 256> UnBase64Impl(IndexSequence<I...>,
|
||||||
|
const char* const base64) {
|
||||||
|
return {UnBase64Impl(I, base64, 0)...};
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr std::array<char, 256> UnBase64(const char* const base64) {
|
||||||
|
return UnBase64Impl(MakeIndexSequence<256>{}, base64);
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr char kBase64[] =
|
||||||
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||||
|
static constexpr std::array<char, 256> kUnBase64 = UnBase64(kBase64);
|
||||||
|
|
||||||
|
bool Base64Unescape(const std::string& encoded, std::string* decoded) {
|
||||||
|
decoded->clear();
|
||||||
|
size_t encoded_len = encoded.size();
|
||||||
|
decoded->reserve(3 * (encoded_len / 4) + (encoded_len % 4));
|
||||||
|
int bit_pos = 0;
|
||||||
|
char dst = 0;
|
||||||
|
for (int src : encoded) {
|
||||||
|
if (std::isspace(src) || src == '=') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
char src_bin = kUnBase64[src];
|
||||||
|
if (src_bin >= 64) {
|
||||||
|
decoded->clear();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (bit_pos == 0) {
|
||||||
|
dst |= src_bin << 2;
|
||||||
|
bit_pos = 6;
|
||||||
|
} else {
|
||||||
|
dst |= static_cast<char>(src_bin >> (bit_pos - 2));
|
||||||
|
decoded->push_back(dst);
|
||||||
|
dst = static_cast<char>(src_bin << (10 - bit_pos));
|
||||||
|
bit_pos = (bit_pos + 6) % 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
} // namespace testing
|
} // namespace testing
|
||||||
|
@ -716,6 +716,46 @@ TEST(FunctionTest, LongArgumentList) {
|
|||||||
F::MakeResultIgnoredValue>::value));
|
F::MakeResultIgnoredValue>::value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(Base64Unescape, InvalidString) {
|
||||||
|
std::string unescaped;
|
||||||
|
EXPECT_FALSE(Base64Unescape("(invalid)", &unescaped));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Base64Unescape, ShortString) {
|
||||||
|
std::string unescaped;
|
||||||
|
EXPECT_TRUE(Base64Unescape("SGVsbG8gd29ybGQh", &unescaped));
|
||||||
|
EXPECT_EQ("Hello world!", unescaped);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Base64Unescape, ShortStringWithPadding) {
|
||||||
|
std::string unescaped;
|
||||||
|
EXPECT_TRUE(Base64Unescape("SGVsbG8gd29ybGQ=", &unescaped));
|
||||||
|
EXPECT_EQ("Hello world", unescaped);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Base64Unescape, ShortStringWithoutPadding) {
|
||||||
|
std::string unescaped;
|
||||||
|
EXPECT_TRUE(Base64Unescape("SGVsbG8gd29ybGQ", &unescaped));
|
||||||
|
EXPECT_EQ("Hello world", unescaped);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Base64Unescape, LongStringWithWhiteSpaces) {
|
||||||
|
std::string escaped =
|
||||||
|
R"(TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz
|
||||||
|
IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg
|
||||||
|
dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu
|
||||||
|
dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo
|
||||||
|
ZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=)";
|
||||||
|
std::string expected =
|
||||||
|
"Man is distinguished, not only by his reason, but by this singular "
|
||||||
|
"passion from other animals, which is a lust of the mind, that by a "
|
||||||
|
"perseverance of delight in the continued and indefatigable generation "
|
||||||
|
"of knowledge, exceeds the short vehemence of any carnal pleasure.";
|
||||||
|
std::string unescaped;
|
||||||
|
EXPECT_TRUE(Base64Unescape(escaped, &unescaped));
|
||||||
|
EXPECT_EQ(expected, unescaped);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
} // namespace testing
|
} // namespace testing
|
||||||
|
@ -1866,6 +1866,33 @@ TEST(EndsWithTest, CanDescribeSelf) {
|
|||||||
EXPECT_EQ("ends with \"Hi\"", Describe(m));
|
EXPECT_EQ("ends with \"Hi\"", Describe(m));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tests WhenBase64Unescaped.
|
||||||
|
|
||||||
|
TEST(WhenBase64UnescapedTest, MatchesUnescapedBase64Strings) {
|
||||||
|
const Matcher<const char*> m1 = WhenBase64Unescaped(EndsWith("!"));
|
||||||
|
EXPECT_FALSE(m1.Matches("invalid base64"));
|
||||||
|
EXPECT_FALSE(m1.Matches("aGVsbG8gd29ybGQ=")); // hello world
|
||||||
|
EXPECT_TRUE(m1.Matches("aGVsbG8gd29ybGQh")); // hello world!
|
||||||
|
|
||||||
|
const Matcher<const std::string&> m2 = WhenBase64Unescaped(EndsWith("!"));
|
||||||
|
EXPECT_FALSE(m2.Matches("invalid base64"));
|
||||||
|
EXPECT_FALSE(m2.Matches("aGVsbG8gd29ybGQ=")); // hello world
|
||||||
|
EXPECT_TRUE(m2.Matches("aGVsbG8gd29ybGQh")); // hello world!
|
||||||
|
|
||||||
|
#if GTEST_INTERNAL_HAS_STRING_VIEW
|
||||||
|
const Matcher<const internal::StringView&> m3 =
|
||||||
|
WhenBase64Unescaped(EndsWith("!"));
|
||||||
|
EXPECT_FALSE(m3.Matches("invalid base64"));
|
||||||
|
EXPECT_FALSE(m3.Matches("aGVsbG8gd29ybGQ=")); // hello world
|
||||||
|
EXPECT_TRUE(m3.Matches("aGVsbG8gd29ybGQh")); // hello world!
|
||||||
|
#endif // GTEST_INTERNAL_HAS_STRING_VIEW
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(WhenBase64UnescapedTest, CanDescribeSelf) {
|
||||||
|
const Matcher<const char*> m = WhenBase64Unescaped(EndsWith("!"));
|
||||||
|
EXPECT_EQ("matches after Base64Unescape ends with \"!\"", Describe(m));
|
||||||
|
}
|
||||||
|
|
||||||
// Tests MatchesRegex().
|
// Tests MatchesRegex().
|
||||||
|
|
||||||
TEST(MatchesRegexTest, MatchesStringMatchingGivenRegex) {
|
TEST(MatchesRegexTest, MatchesStringMatchingGivenRegex) {
|
||||||
|
Loading…
Reference in New Issue
Block a user