| // |
| // Copyright 2021 gRPC authors. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // |
| |
| #include "src/core/lib/transport/parsed_metadata.h" |
| |
| #include <memory> |
| |
| #include "absl/strings/numbers.h" |
| #include "gmock/gmock.h" |
| #include "gtest/gtest.h" |
| |
| #include <grpc/support/log.h> |
| |
| #include "src/core/lib/transport/metadata_batch.h" |
| #include "test/core/util/test_config.h" |
| |
| namespace grpc_core { |
| namespace testing { |
| |
| struct CharTrait { |
| using MementoType = char; |
| static absl::string_view key() { return "key"; } |
| static char test_memento() { return 'a'; } |
| static char test_value() { return 'a'; } |
| static size_t test_memento_transport_size() { return 34; } |
| static char MementoToValue(char memento) { return memento; } |
| static char ParseMemento(Slice slice, MetadataParseErrorFn) { |
| return slice[0]; |
| } |
| static std::string DisplayValue(char value) { return std::string(1, value); } |
| static std::string DisplayMemento(MementoType memento) { |
| return DisplayValue(memento); |
| } |
| }; |
| |
| struct Int32Trait { |
| using MementoType = int32_t; |
| static absl::string_view key() { return "key2"; } |
| static int32_t test_memento() { return -1; } |
| static int32_t test_value() { return -1; } |
| static size_t test_memento_transport_size() { return 478; } |
| static int32_t MementoToValue(int32_t memento) { return memento; } |
| static int32_t ParseMemento(Slice slice, MetadataParseErrorFn) { |
| int32_t out; |
| GPR_ASSERT(absl::SimpleAtoi(slice.as_string_view(), &out)); |
| return out; |
| } |
| static std::string DisplayValue(int32_t value) { |
| return std::to_string(value); |
| } |
| static std::string DisplayMemento(MementoType memento) { |
| return DisplayValue(memento); |
| } |
| }; |
| |
| struct Int64Trait { |
| using MementoType = int64_t; |
| static absl::string_view key() { return "key3"; } |
| static int64_t test_memento() { return 83481847284179298; } |
| static int64_t test_value() { return -83481847284179298; } |
| static size_t test_memento_transport_size() { return 87; } |
| static int64_t MementoToValue(int64_t memento) { return -memento; } |
| static int64_t ParseMemento(Slice slice, MetadataParseErrorFn) { |
| int64_t out; |
| GPR_ASSERT(absl::SimpleAtoi(slice.as_string_view(), &out)); |
| return out; |
| } |
| static std::string DisplayValue(int64_t value) { |
| return std::to_string(value); |
| } |
| static std::string DisplayMemento(MementoType memento) { |
| return DisplayValue(memento); |
| } |
| }; |
| |
| struct IntptrTrait { |
| using MementoType = intptr_t; |
| static absl::string_view key() { return "key4"; } |
| static intptr_t test_memento() { return 8374298; } |
| static intptr_t test_value() { return test_memento() / 2; } |
| static size_t test_memento_transport_size() { return 800; } |
| static intptr_t MementoToValue(intptr_t memento) { return memento / 2; } |
| static intptr_t ParseMemento(Slice slice, MetadataParseErrorFn) { |
| intptr_t out; |
| GPR_ASSERT(absl::SimpleAtoi(slice.as_string_view(), &out)); |
| return out; |
| } |
| static std::string DisplayValue(intptr_t value) { |
| return std::to_string(value); |
| } |
| static std::string DisplayMemento(MementoType memento) { |
| return DisplayValue(memento); |
| } |
| }; |
| |
| struct StringTrait { |
| using MementoType = std::string; |
| static absl::string_view key() { return "key5-bin"; } |
| static std::string test_memento() { return "hello"; } |
| static std::string test_value() { return "hi hello"; } |
| static size_t test_memento_transport_size() { return 599; } |
| static std::string MementoToValue(std::string memento) { |
| return "hi " + memento; |
| } |
| static std::string ParseMemento(Slice slice, MetadataParseErrorFn) { |
| auto view = slice.as_string_view(); |
| return std::string(view.begin(), view.end()); |
| } |
| static std::string DisplayValue(const std::string& value) { return value; } |
| static std::string DisplayMemento(MementoType memento) { |
| return DisplayValue(memento); |
| } |
| }; |
| |
| class FakeContainer { |
| public: |
| void Set(CharTrait, char x) { SetChar(x); } |
| void Set(Int32Trait, int32_t x) { SetInt32(x); } |
| void Set(Int64Trait, int64_t x) { SetInt64(x); } |
| void Set(IntptrTrait, intptr_t x) { SetIntptr(x); } |
| void Set(StringTrait, std::string x) { SetString(x); } |
| |
| void Set(const ParsedMetadata<FakeContainer>& metadata) { |
| metadata.SetOnContainer(this); |
| } |
| |
| MOCK_METHOD1(SetChar, void(char)); |
| MOCK_METHOD1(SetInt32, void(int32_t)); |
| MOCK_METHOD1(SetInt64, void(int64_t)); |
| MOCK_METHOD1(SetIntptr, void(intptr_t)); |
| MOCK_METHOD1(SetString, void(std::string)); |
| }; |
| |
| using FakeParsedMetadata = ::grpc_core::ParsedMetadata<FakeContainer>; |
| |
| TEST(ParsedMetadataTest, Noop) { FakeParsedMetadata(); } |
| |
| TEST(ParsedMetadataTest, DebugString) { |
| FakeParsedMetadata parsed(CharTrait(), 'x', 36); |
| EXPECT_EQ(parsed.DebugString(), "key: x"); |
| } |
| |
| TEST(ParsedMetadataTest, IsNotBinary) { |
| FakeParsedMetadata parsed(CharTrait(), 'x', 36); |
| EXPECT_FALSE(parsed.is_binary_header()); |
| } |
| |
| TEST(ParsedMetadataTest, IsBinary) { |
| FakeParsedMetadata parsed(StringTrait(), "s", 36); |
| EXPECT_TRUE(parsed.is_binary_header()); |
| } |
| |
| TEST(ParsedMetadataTest, Set) { |
| FakeContainer c; |
| FakeParsedMetadata p(CharTrait(), 'x', 36); |
| EXPECT_CALL(c, SetChar('x')).Times(1); |
| c.Set(p); |
| p = FakeParsedMetadata(Int32Trait(), -1, 478); |
| EXPECT_CALL(c, SetInt32(-1)).Times(1); |
| c.Set(p); |
| p = FakeParsedMetadata(Int64Trait(), 83481847284179298, 87); |
| EXPECT_CALL(c, SetInt64(-83481847284179298)).Times(1); |
| c.Set(p); |
| p = FakeParsedMetadata(IntptrTrait(), 8374298, 800); |
| EXPECT_CALL(c, SetIntptr(4187149)).Times(1); |
| c.Set(p); |
| p = FakeParsedMetadata(StringTrait(), "hello", 599); |
| EXPECT_CALL(c, SetString("hi hello")).Times(1); |
| c.Set(p); |
| } |
| |
| template <typename T> |
| class TraitSpecializedTest : public ::testing::Test {}; |
| |
| TYPED_TEST_SUITE_P(TraitSpecializedTest); |
| |
| TYPED_TEST_P(TraitSpecializedTest, Noop) { |
| FakeParsedMetadata(TypeParam(), TypeParam::test_memento(), |
| TypeParam::test_memento_transport_size()); |
| } |
| |
| TYPED_TEST_P(TraitSpecializedTest, CanMove) { |
| FakeParsedMetadata a(TypeParam(), TypeParam::test_memento(), |
| TypeParam::test_memento_transport_size()); |
| FakeParsedMetadata b = std::move(a); |
| a = std::move(b); |
| } |
| |
| TYPED_TEST_P(TraitSpecializedTest, DebugString) { |
| FakeParsedMetadata p(TypeParam(), TypeParam::test_memento(), |
| TypeParam::test_memento_transport_size()); |
| EXPECT_EQ(p.DebugString(), |
| absl::StrCat(TypeParam::key(), ": ", |
| TypeParam::DisplayValue(TypeParam::test_memento()))); |
| } |
| |
| TYPED_TEST_P(TraitSpecializedTest, TransportSize) { |
| FakeParsedMetadata p(TypeParam(), TypeParam::test_memento(), |
| TypeParam::test_memento_transport_size()); |
| EXPECT_EQ(p.transport_size(), TypeParam::test_memento_transport_size()); |
| } |
| |
| REGISTER_TYPED_TEST_SUITE_P(TraitSpecializedTest, Noop, CanMove, DebugString, |
| TransportSize); |
| |
| using InterestingTraits = ::testing::Types<CharTrait, Int32Trait, Int64Trait, |
| IntptrTrait, StringTrait>; |
| INSTANTIATE_TYPED_TEST_SUITE_P(My, TraitSpecializedTest, InterestingTraits); |
| |
| TEST(KeyValueTest, Simple) { |
| using PM = ParsedMetadata<grpc_metadata_batch>; |
| using PMPtr = std::unique_ptr<PM>; |
| PMPtr p = |
| std::make_unique<PM>(PM::FromSlicePair{}, Slice::FromCopiedString("key"), |
| Slice::FromCopiedString("value"), 40); |
| EXPECT_EQ(p->DebugString(), "key: value"); |
| EXPECT_EQ(p->transport_size(), 40); |
| PM p2 = p->WithNewValue( |
| Slice::FromCopiedString("some_other_value"), strlen("some_other_value"), |
| [](absl::string_view msg, const Slice& value) { |
| ASSERT_TRUE(false) << "Should not be called: msg=" << msg |
| << ", value=" << value.as_string_view(); |
| }); |
| EXPECT_EQ(p->DebugString(), "key: value"); |
| EXPECT_EQ(p2.DebugString(), "key: some_other_value"); |
| EXPECT_EQ(p2.transport_size(), 51); |
| p.reset(); |
| EXPECT_EQ(p2.DebugString(), "key: some_other_value"); |
| EXPECT_EQ(p2.transport_size(), 51); |
| PM p3 = std::move(p2); |
| EXPECT_EQ(p3.DebugString(), "key: some_other_value"); |
| EXPECT_EQ(p3.transport_size(), 51); |
| } |
| |
| TEST(KeyValueTest, LongKey) { |
| using PM = ParsedMetadata<grpc_metadata_batch>; |
| using PMPtr = std::unique_ptr<PM>; |
| PMPtr p = std::make_unique<PM>(PM::FromSlicePair{}, |
| Slice::FromCopiedString(std::string(60, 'a')), |
| Slice::FromCopiedString("value"), 60 + 5 + 32); |
| EXPECT_EQ( |
| p->DebugString(), |
| "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: value"); |
| EXPECT_EQ(p->transport_size(), 97); |
| PM p2 = p->WithNewValue( |
| Slice::FromCopiedString("some_other_value"), strlen("some_other_value"), |
| [](absl::string_view msg, const Slice& value) { |
| ASSERT_TRUE(false) << "Should not be called: msg=" << msg |
| << ", value=" << value.as_string_view(); |
| }); |
| EXPECT_EQ( |
| p->DebugString(), |
| "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: value"); |
| EXPECT_EQ(p2.DebugString(), |
| "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: " |
| "some_other_value"); |
| EXPECT_EQ(p2.transport_size(), 108); |
| p.reset(); |
| EXPECT_EQ(p2.DebugString(), |
| "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: " |
| "some_other_value"); |
| EXPECT_EQ(p2.transport_size(), 108); |
| PM p3 = std::move(p2); |
| EXPECT_EQ(p3.DebugString(), |
| "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: " |
| "some_other_value"); |
| EXPECT_EQ(p3.transport_size(), 108); |
| } |
| |
| } // namespace testing |
| } // namespace grpc_core |
| |
| int main(int argc, char** argv) { |
| testing::InitGoogleTest(&argc, argv); |
| grpc::testing::TestEnvironment env(&argc, argv); |
| return RUN_ALL_TESTS(); |
| }; |