| /* |
| * |
| * Copyright 2015 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/channel/channel_args.h" |
| |
| #include <string.h> |
| |
| #include "gtest/gtest.h" |
| |
| #include <grpc/grpc.h> |
| #include <grpc/grpc_security.h> |
| #include <grpc/support/alloc.h> |
| #include <grpc/support/log.h> |
| |
| #include "src/core/lib/gpr/useful.h" |
| #include "src/core/lib/gprpp/ref_counted.h" |
| #include "src/core/lib/gprpp/ref_counted_ptr.h" |
| #include "src/core/lib/iomgr/exec_ctx.h" |
| #include "test/core/util/test_config.h" |
| |
| namespace grpc_core { |
| |
| TEST(ChannelArgsTest, Noop) { ChannelArgs(); } |
| |
| TEST(ChannelArgsTest, SetGetRemove) { |
| const grpc_arg_pointer_vtable malloc_vtable = { |
| // copy |
| [](void* p) { return p; }, |
| // destroy |
| [](void*) {}, |
| // equal |
| [](void* p1, void* p2) { return QsortCompare(p1, p2); }, |
| }; |
| void* ptr = gpr_malloc(42); |
| |
| ChannelArgs a; |
| ChannelArgs b = a.Set("answer", 42); |
| ChannelArgs c = b.Set("foo", "bar"); |
| ChannelArgs d = c.Set("ptr", ChannelArgs::Pointer(ptr, &malloc_vtable)); |
| ChannelArgs e = d.Set("alpha", "beta"); |
| ChannelArgs f = e.Remove("answer"); |
| EXPECT_EQ(a.Get("answer"), nullptr); |
| EXPECT_EQ(*b.Get("answer"), ChannelArgs::Value(42)); |
| EXPECT_EQ(*c.Get("answer"), ChannelArgs::Value(42)); |
| EXPECT_EQ(c.GetInt("answer"), 42); |
| EXPECT_EQ(c.GetString("answer"), absl::nullopt); |
| EXPECT_EQ(f.Get("answer"), nullptr); |
| EXPECT_EQ(*c.Get("foo"), ChannelArgs::Value("bar")); |
| EXPECT_EQ(c.GetString("foo"), "bar"); |
| EXPECT_EQ(c.GetString("answer"), absl::nullopt); |
| EXPECT_EQ(*d.Get("ptr"), |
| ChannelArgs::Value(ChannelArgs::Pointer(ptr, &malloc_vtable))); |
| EXPECT_EQ(*e.Get("alpha"), ChannelArgs::Value("beta")); |
| gpr_free(ptr); |
| } |
| |
| TEST(ChannelArgsTest, StoreRefCountedPtr) { |
| struct Test : public RefCounted<Test> { |
| explicit Test(int n) : n(n) {} |
| int n; |
| static int ChannelArgsCompare(const Test* a, const Test* b) { |
| return a->n - b->n; |
| } |
| }; |
| auto p = MakeRefCounted<Test>(123); |
| |
| ChannelArgs a; |
| a = a.Set("test", p); |
| EXPECT_EQ(a.GetPointer<Test>("test")->n, 123); |
| } |
| |
| TEST(ChannelArgsTest, ObjectApi) { |
| struct MyFancyObject : public RefCounted<MyFancyObject> { |
| explicit MyFancyObject(int n) : n(n) {} |
| static absl::string_view ChannelArgName() { |
| return "grpc.internal.my-fancy-object"; |
| } |
| int n; |
| static int ChannelArgsCompare(const MyFancyObject* a, |
| const MyFancyObject* b) { |
| return a->n - b->n; |
| } |
| }; |
| auto p = MakeRefCounted<MyFancyObject>(42); |
| ChannelArgs a; |
| a = a.SetObject(std::move(p)); |
| EXPECT_EQ(a.GetObject<MyFancyObject>()->n, 42); |
| } |
| |
| TEST(ChannelArgsTest, ToAndFromC) { |
| const grpc_arg_pointer_vtable malloc_vtable = { |
| // copy |
| [](void* p) { return p; }, |
| // destroy |
| [](void*) {}, |
| // equal |
| [](void* p1, void* p2) { return QsortCompare(p1, p2); }, |
| }; |
| void* ptr = gpr_malloc(42); |
| ChannelArgs a = ChannelArgs() |
| .Set("answer", 42) |
| .Set("foo", "bar") |
| .Set("ptr", ChannelArgs::Pointer(ptr, &malloc_vtable)) |
| .Set("alpha", "beta"); |
| ChannelArgs b = ChannelArgs::FromC(a.ToC().get()); |
| EXPECT_EQ(a, b); |
| gpr_free(ptr); |
| } |
| |
| // shared_ptrs in ChannelArgs must support enable_shared_from_this |
| class ShareableObject : public std::enable_shared_from_this<ShareableObject> { |
| public: |
| explicit ShareableObject(int n) : n(n) {} |
| int n; |
| static int ChannelArgsCompare(const ShareableObject* a, |
| const ShareableObject* b) { |
| return a->n - b->n; |
| } |
| static absl::string_view ChannelArgName() { return "grpc.test"; } |
| }; |
| |
| TEST(ChannelArgsTest, StoreAndRetrieveSharedPtr) { |
| std::shared_ptr<ShareableObject> copied_obj; |
| { |
| ChannelArgs channel_args; |
| auto shared_obj = std::make_shared<ShareableObject>(42); |
| EXPECT_TRUE(shared_obj.unique()); |
| channel_args = channel_args.SetObject(shared_obj); |
| EXPECT_FALSE(shared_obj.unique()); |
| copied_obj = channel_args.GetObjectRef<ShareableObject>(); |
| EXPECT_EQ(copied_obj->n, 42); |
| // Refs: p, copied_obj, and ChannelArgs |
| EXPECT_EQ(3, copied_obj.use_count()); |
| } |
| // The p and ChannelArgs are deleted. |
| EXPECT_TRUE(copied_obj.unique()); |
| EXPECT_EQ(copied_obj->n, 42); |
| } |
| |
| TEST(ChannelArgsTest, RetrieveRawPointerFromStoredSharedPtr) { |
| ChannelArgs channel_args; |
| auto shared_obj = std::make_shared<ShareableObject>(42); |
| EXPECT_TRUE(shared_obj.unique()); |
| channel_args = channel_args.SetObject(shared_obj); |
| EXPECT_FALSE(shared_obj.unique()); |
| ShareableObject* raw_obj = channel_args.GetObject<ShareableObject>(); |
| EXPECT_EQ(raw_obj->n, 42); |
| // Refs: p and ChannelArgs |
| EXPECT_EQ(2, shared_obj.use_count()); |
| } |
| |
| } // namespace grpc_core |
| |
| TEST(GrpcChannelArgsTest, Create) { |
| grpc_core::ExecCtx exec_ctx; |
| grpc_arg to_add[2]; |
| grpc_channel_args* ch_args; |
| |
| to_add[0] = |
| grpc_channel_arg_integer_create(const_cast<char*>("int_arg"), 123); |
| to_add[1] = grpc_channel_arg_string_create(const_cast<char*>("str key"), |
| const_cast<char*>("str value")); |
| ch_args = grpc_channel_args_copy_and_add(nullptr, to_add, 2); |
| |
| GPR_ASSERT(ch_args->num_args == 2); |
| GPR_ASSERT(strcmp(ch_args->args[0].key, to_add[0].key) == 0); |
| GPR_ASSERT(ch_args->args[0].type == to_add[0].type); |
| GPR_ASSERT(ch_args->args[0].value.integer == to_add[0].value.integer); |
| |
| GPR_ASSERT(strcmp(ch_args->args[1].key, to_add[1].key) == 0); |
| GPR_ASSERT(ch_args->args[1].type == to_add[1].type); |
| GPR_ASSERT(strcmp(ch_args->args[1].value.string, to_add[1].value.string) == |
| 0); |
| |
| grpc_channel_args_destroy(ch_args); |
| } |
| |
| struct fake_class { |
| int foo; |
| }; |
| |
| static void* fake_pointer_arg_copy(void* arg) { |
| gpr_log(GPR_DEBUG, "fake_pointer_arg_copy"); |
| fake_class* fc = static_cast<fake_class*>(arg); |
| fake_class* new_fc = static_cast<fake_class*>(gpr_malloc(sizeof(fake_class))); |
| new_fc->foo = fc->foo; |
| return new_fc; |
| } |
| |
| static void fake_pointer_arg_destroy(void* arg) { |
| gpr_log(GPR_DEBUG, "fake_pointer_arg_destroy"); |
| fake_class* fc = static_cast<fake_class*>(arg); |
| gpr_free(fc); |
| } |
| |
| static int fake_pointer_cmp(void* a, void* b) { |
| return grpc_core::QsortCompare(a, b); |
| } |
| |
| static const grpc_arg_pointer_vtable fake_pointer_arg_vtable = { |
| fake_pointer_arg_copy, fake_pointer_arg_destroy, fake_pointer_cmp}; |
| |
| TEST(GrpcChannelArgsTest, ChannelCreateWithArgs) { |
| grpc_arg client_a[3]; |
| |
| client_a[0] = |
| grpc_channel_arg_integer_create(const_cast<char*>("arg_int"), 0); |
| client_a[1] = grpc_channel_arg_string_create( |
| const_cast<char*>("arg_str"), const_cast<char*>("arg_str_val")); |
| // allocated and adds custom pointer arg |
| fake_class* fc = static_cast<fake_class*>(gpr_malloc(sizeof(fake_class))); |
| fc->foo = 42; |
| client_a[2] = grpc_channel_arg_pointer_create( |
| const_cast<char*>("arg_pointer"), fc, &fake_pointer_arg_vtable); |
| |
| // creates channel |
| grpc_channel_args client_args = {GPR_ARRAY_SIZE(client_a), client_a}; |
| grpc_channel_credentials* creds = grpc_insecure_credentials_create(); |
| grpc_channel* c = grpc_channel_create("fake_target", creds, &client_args); |
| grpc_channel_credentials_release(creds); |
| // user is can free the memory they allocated here |
| gpr_free(fc); |
| grpc_channel_destroy(c); |
| } |
| |
| grpc_channel_args* mutate_channel_args(const char* target, |
| grpc_channel_args* old_args, |
| grpc_channel_stack_type /*type*/) { |
| GPR_ASSERT(old_args != nullptr); |
| GPR_ASSERT(grpc_channel_args_find(old_args, "arg_int")->value.integer == 0); |
| GPR_ASSERT(strcmp(grpc_channel_args_find(old_args, "arg_str")->value.string, |
| "arg_str_val") == 0); |
| GPR_ASSERT( |
| grpc_channel_args_find(old_args, "arg_pointer")->value.pointer.vtable == |
| &fake_pointer_arg_vtable); |
| |
| if (strcmp(target, "no_op_mutator") == 0) { |
| return old_args; |
| } |
| |
| GPR_ASSERT(strcmp(target, "minimal_stack_mutator") == 0); |
| const char* args_to_remove[] = {"arg_int", "arg_str", "arg_pointer"}; |
| |
| grpc_arg no_deadline_filter_arg = grpc_channel_arg_integer_create( |
| const_cast<char*>(GRPC_ARG_MINIMAL_STACK), 1); |
| grpc_channel_args* new_args = nullptr; |
| new_args = grpc_channel_args_copy_and_add_and_remove( |
| old_args, args_to_remove, GPR_ARRAY_SIZE(args_to_remove), |
| &no_deadline_filter_arg, 1); |
| grpc_channel_args_destroy(old_args); |
| return new_args; |
| } |
| |
| TEST(GrpcChannelArgsTest, TestServerCreateWithArgs) { |
| grpc_arg server_a[3]; |
| |
| // adds integer arg |
| server_a[0].type = GRPC_ARG_INTEGER; |
| server_a[0].key = const_cast<char*>("arg_int"); |
| server_a[0].value.integer = 0; |
| |
| // adds const str arg |
| server_a[1].type = GRPC_ARG_STRING; |
| server_a[1].key = const_cast<char*>("arg_str"); |
| server_a[1].value.string = const_cast<char*>("arg_str_val"); |
| |
| // allocated and adds custom pointer arg |
| fake_class* fc = static_cast<fake_class*>(gpr_malloc(sizeof(fake_class))); |
| fc->foo = 42; |
| server_a[2].type = GRPC_ARG_POINTER; |
| server_a[2].key = const_cast<char*>("arg_pointer"); |
| server_a[2].value.pointer.vtable = &fake_pointer_arg_vtable; |
| server_a[2].value.pointer.p = fc; |
| |
| // creates server |
| grpc_channel_args server_args = {GPR_ARRAY_SIZE(server_a), server_a}; |
| grpc_server* s = grpc_server_create(&server_args, nullptr); |
| // user is can free the memory they allocated here |
| gpr_free(fc); |
| grpc_server_destroy(s); |
| } |
| |
| int main(int argc, char** argv) { |
| ::testing::InitGoogleTest(&argc, argv); |
| grpc::testing::TestEnvironment env(&argc, argv); |
| grpc_init(); |
| auto r = RUN_ALL_TESTS(); |
| grpc_shutdown(); |
| return r; |
| } |