mirror of https://github.com/grpc/grpc.git
parent
d89c202dca
commit
e92294c587
7 changed files with 0 additions and 1674 deletions
@ -1,82 +0,0 @@ |
||||
# Copyright 2016 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. |
||||
|
||||
load("//bazel:grpc_build_system.bzl", "grpc_cc_library", "grpc_cc_test", "grpc_cc_binary", "grpc_package") |
||||
|
||||
grpc_package(name = "test/core/census") |
||||
|
||||
licenses(["notice"]) # Apache v2 |
||||
|
||||
grpc_cc_test( |
||||
name = "context_test", |
||||
srcs = ["context_test.cc"], |
||||
language = "C++", |
||||
deps = [ |
||||
"//:gpr", |
||||
"//:grpc", |
||||
"//test/core/util:gpr_test_util", |
||||
"//test/core/util:grpc_test_util", |
||||
], |
||||
) |
||||
|
||||
grpc_cc_test( |
||||
name = "mlog_test", |
||||
srcs = ["mlog_test.cc"], |
||||
language = "C++", |
||||
deps = [ |
||||
"//:gpr", |
||||
"//:grpc", |
||||
"//test/core/util:gpr_test_util", |
||||
"//test/core/util:grpc_test_util", |
||||
], |
||||
) |
||||
|
||||
grpc_cc_test( |
||||
name = "resource_test", |
||||
srcs = ["resource_test.cc"], |
||||
language = "C++", |
||||
data = [ |
||||
":data/resource_empty_name.pb", |
||||
":data/resource_full.pb", |
||||
":data/resource_minimal_good.pb", |
||||
":data/resource_no_name.pb", |
||||
":data/resource_no_numerator.pb", |
||||
":data/resource_no_unit.pb", |
||||
], |
||||
deps = [ |
||||
"//:gpr", |
||||
"//:grpc", |
||||
"//test/core/util:gpr_test_util", |
||||
"//test/core/util:grpc_test_util", |
||||
], |
||||
) |
||||
|
||||
grpc_cc_test( |
||||
name = "trace_context_test", |
||||
srcs = ["trace_context_test.cc"], |
||||
language = "C++", |
||||
data = [ |
||||
":data/context_empty.pb", |
||||
":data/context_full.pb", |
||||
":data/context_no_span_options.pb", |
||||
":data/context_span_only.pb", |
||||
":data/context_trace_only.pb", |
||||
], |
||||
deps = [ |
||||
"//:gpr", |
||||
"//:grpc", |
||||
"//test/core/util:gpr_test_util", |
||||
"//test/core/util:grpc_test_util", |
||||
], |
||||
) |
@ -1,363 +0,0 @@ |
||||
/*
|
||||
* |
||||
* 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. |
||||
* |
||||
*/ |
||||
|
||||
// Test census_context functions, including encoding/decoding
|
||||
|
||||
#include <grpc/census.h> |
||||
#include <grpc/support/log.h> |
||||
#include <grpc/support/time.h> |
||||
#include <stdbool.h> |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#include "test/core/util/test_config.h" |
||||
|
||||
// A set of tags Used to create a basic context for testing. Note that
|
||||
// replace_add_delete_test() relies on specific offsets into this array - if
|
||||
// you add or delete entries, you will also need to change the test.
|
||||
#define BASIC_TAG_COUNT 8 |
||||
static census_tag basic_tags[BASIC_TAG_COUNT] = { |
||||
/* 0 */ {"key0", "tag value", 0}, |
||||
/* 1 */ {"k1", "a", CENSUS_TAG_PROPAGATE}, |
||||
/* 2 */ {"k2", "a longer tag value supercalifragilisticexpialiadocious", |
||||
CENSUS_TAG_STATS}, |
||||
/* 3 */ {"key_three", "", 0}, |
||||
/* 4 */ {"a_really_really_really_really_long_key_4", "random", |
||||
CENSUS_TAG_PROPAGATE | CENSUS_TAG_STATS}, |
||||
/* 5 */ {"k5", "v5", CENSUS_TAG_PROPAGATE}, |
||||
/* 6 */ {"k6", "v6", CENSUS_TAG_STATS}, |
||||
/* 7 */ {"k7", "v7", CENSUS_TAG_PROPAGATE | CENSUS_TAG_STATS}}; |
||||
|
||||
// Set of tags used to modify the basic context. Note that
|
||||
// replace_add_delete_test() relies on specific offsets into this array - if
|
||||
// you add or delete entries, you will also need to change the test. Other
|
||||
// tests that rely on specific instances have XXX_XXX_OFFSET definitions (also
|
||||
// change the defines below if you add/delete entires).
|
||||
#define MODIFY_TAG_COUNT 10 |
||||
static census_tag modify_tags[MODIFY_TAG_COUNT] = { |
||||
#define REPLACE_VALUE_OFFSET 0 |
||||
/* 0 */ {"key0", "replace key0", 0}, // replaces tag value only
|
||||
#define ADD_TAG_OFFSET 1 |
||||
/* 1 */ {"new_key", "xyzzy", CENSUS_TAG_STATS}, // new tag
|
||||
#define DELETE_TAG_OFFSET 2 |
||||
/* 2 */ {"k5", NULL, 0}, // should delete tag
|
||||
/* 3 */ {"k5", NULL, 0}, // try deleting already-deleted tag
|
||||
/* 4 */ {"non-existent", NULL, 0}, // delete non-existent tag
|
||||
#define REPLACE_FLAG_OFFSET 5 |
||||
/* 5 */ {"k1", "a", 0}, // change flags only
|
||||
/* 6 */ {"k7", "bar", CENSUS_TAG_STATS}, // change flags and value
|
||||
/* 7 */ {"k2", "", CENSUS_TAG_PROPAGATE}, // more value and flags change
|
||||
/* 8 */ {"k5", "bar", 0}, // add back tag, with different value
|
||||
/* 9 */ {"foo", "bar", CENSUS_TAG_PROPAGATE}, // another new tag
|
||||
}; |
||||
|
||||
// Utility function to compare tags. Returns true if all fields match.
|
||||
static bool compare_tag(const census_tag *t1, const census_tag *t2) { |
||||
return (strcmp(t1->key, t2->key) == 0 && strcmp(t1->value, t2->value) == 0 && |
||||
t1->flags == t2->flags); |
||||
} |
||||
|
||||
// Utility function to validate a tag exists in context.
|
||||
static bool validate_tag(const census_context *context, const census_tag *tag) { |
||||
census_tag tag2; |
||||
if (census_context_get_tag(context, tag->key, &tag2) != 1) return false; |
||||
return compare_tag(tag, &tag2); |
||||
} |
||||
|
||||
// Create an empty context.
|
||||
static void empty_test(void) { |
||||
struct census_context *context = census_context_create(NULL, NULL, 0, NULL); |
||||
GPR_ASSERT(context != NULL); |
||||
const census_context_status *status = census_context_get_status(context); |
||||
census_context_status expected = {0, 0, 0, 0, 0, 0, 0}; |
||||
GPR_ASSERT(memcmp(status, &expected, sizeof(expected)) == 0); |
||||
census_context_destroy(context); |
||||
} |
||||
|
||||
// Test create and iteration over basic context.
|
||||
static void basic_test(void) { |
||||
const census_context_status *status; |
||||
struct census_context *context = |
||||
census_context_create(NULL, basic_tags, BASIC_TAG_COUNT, &status); |
||||
census_context_status expected = {4, 4, 0, 8, 0, 0, 0}; |
||||
GPR_ASSERT(memcmp(status, &expected, sizeof(expected)) == 0); |
||||
census_context_iterator it; |
||||
census_context_initialize_iterator(context, &it); |
||||
census_tag tag; |
||||
while (census_context_next_tag(&it, &tag)) { |
||||
// can't rely on tag return order: make sure it matches exactly one.
|
||||
int matches = 0; |
||||
for (int i = 0; i < BASIC_TAG_COUNT; i++) { |
||||
if (compare_tag(&tag, &basic_tags[i])) matches++; |
||||
} |
||||
GPR_ASSERT(matches == 1); |
||||
} |
||||
census_context_destroy(context); |
||||
} |
||||
|
||||
// Test census_context_get_tag().
|
||||
static void lookup_by_key_test(void) { |
||||
struct census_context *context = |
||||
census_context_create(NULL, basic_tags, BASIC_TAG_COUNT, NULL); |
||||
census_tag tag; |
||||
for (int i = 0; i < BASIC_TAG_COUNT; i++) { |
||||
GPR_ASSERT(census_context_get_tag(context, basic_tags[i].key, &tag) == 1); |
||||
GPR_ASSERT(compare_tag(&tag, &basic_tags[i])); |
||||
} |
||||
// non-existent keys
|
||||
GPR_ASSERT(census_context_get_tag(context, "key", &tag) == 0); |
||||
GPR_ASSERT(census_context_get_tag(context, "key01", &tag) == 0); |
||||
GPR_ASSERT(census_context_get_tag(context, "k9", &tag) == 0); |
||||
GPR_ASSERT(census_context_get_tag(context, "random", &tag) == 0); |
||||
GPR_ASSERT(census_context_get_tag(context, "", &tag) == 0); |
||||
census_context_destroy(context); |
||||
} |
||||
|
||||
// Try creating context with invalid entries.
|
||||
static void invalid_test(void) { |
||||
char key[300]; |
||||
memset(key, 'k', 299); |
||||
key[299] = 0; |
||||
char value[300]; |
||||
memset(value, 'v', 299); |
||||
value[299] = 0; |
||||
census_tag tag = {key, value, 0}; |
||||
// long keys, short value. Key lengths (including terminator) should be
|
||||
// <= 255 (CENSUS_MAX_TAG_KV_LEN)
|
||||
value[3] = 0; |
||||
GPR_ASSERT(strlen(value) == 3); |
||||
GPR_ASSERT(strlen(key) == 299); |
||||
const census_context_status *status; |
||||
struct census_context *context = |
||||
census_context_create(NULL, &tag, 1, &status); |
||||
census_context_status expected = {0, 0, 0, 0, 0, 1, 0}; |
||||
GPR_ASSERT(memcmp(status, &expected, sizeof(expected)) == 0); |
||||
census_context_destroy(context); |
||||
key[CENSUS_MAX_TAG_KV_LEN] = 0; |
||||
GPR_ASSERT(strlen(key) == CENSUS_MAX_TAG_KV_LEN); |
||||
context = census_context_create(NULL, &tag, 1, &status); |
||||
GPR_ASSERT(memcmp(status, &expected, sizeof(expected)) == 0); |
||||
census_context_destroy(context); |
||||
key[CENSUS_MAX_TAG_KV_LEN - 1] = 0; |
||||
GPR_ASSERT(strlen(key) == CENSUS_MAX_TAG_KV_LEN - 1); |
||||
context = census_context_create(NULL, &tag, 1, &status); |
||||
census_context_status expected2 = {0, 1, 0, 1, 0, 0, 0}; |
||||
GPR_ASSERT(memcmp(status, &expected2, sizeof(expected2)) == 0); |
||||
census_context_destroy(context); |
||||
// now try with long values
|
||||
value[3] = 'v'; |
||||
GPR_ASSERT(strlen(value) == 299); |
||||
context = census_context_create(NULL, &tag, 1, &status); |
||||
GPR_ASSERT(memcmp(status, &expected, sizeof(expected)) == 0); |
||||
census_context_destroy(context); |
||||
value[CENSUS_MAX_TAG_KV_LEN] = 0; |
||||
GPR_ASSERT(strlen(value) == CENSUS_MAX_TAG_KV_LEN); |
||||
context = census_context_create(NULL, &tag, 1, &status); |
||||
GPR_ASSERT(memcmp(status, &expected, sizeof(expected)) == 0); |
||||
census_context_destroy(context); |
||||
value[CENSUS_MAX_TAG_KV_LEN - 1] = 0; |
||||
GPR_ASSERT(strlen(value) == CENSUS_MAX_TAG_KV_LEN - 1); |
||||
context = census_context_create(NULL, &tag, 1, &status); |
||||
GPR_ASSERT(memcmp(status, &expected2, sizeof(expected2)) == 0); |
||||
census_context_destroy(context); |
||||
// 0 length key.
|
||||
key[0] = 0; |
||||
GPR_ASSERT(strlen(key) == 0); |
||||
context = census_context_create(NULL, &tag, 1, &status); |
||||
GPR_ASSERT(memcmp(status, &expected, sizeof(expected)) == 0); |
||||
census_context_destroy(context); |
||||
// invalid key character
|
||||
key[0] = 31; // 32 (' ') is the first valid character value
|
||||
key[1] = 0; |
||||
GPR_ASSERT(strlen(key) == 1); |
||||
context = census_context_create(NULL, &tag, 1, &status); |
||||
GPR_ASSERT(memcmp(status, &expected, sizeof(expected)) == 0); |
||||
census_context_destroy(context); |
||||
// invalid value character
|
||||
key[0] = ' '; |
||||
value[5] = 127; // 127 (DEL) is ('~' + 1)
|
||||
value[8] = 0; |
||||
GPR_ASSERT(strlen(key) == 1); |
||||
GPR_ASSERT(strlen(value) == 8); |
||||
context = census_context_create(NULL, &tag, 1, &status); |
||||
GPR_ASSERT(memcmp(status, &expected, sizeof(expected)) == 0); |
||||
census_context_destroy(context); |
||||
} |
||||
|
||||
// Make a copy of a context
|
||||
static void copy_test(void) { |
||||
struct census_context *context = |
||||
census_context_create(NULL, basic_tags, BASIC_TAG_COUNT, NULL); |
||||
const census_context_status *status; |
||||
struct census_context *context2 = |
||||
census_context_create(context, NULL, 0, &status); |
||||
census_context_status expected = {4, 4, 0, 0, 0, 0, 0}; |
||||
GPR_ASSERT(memcmp(status, &expected, sizeof(expected)) == 0); |
||||
for (int i = 0; i < BASIC_TAG_COUNT; i++) { |
||||
census_tag tag; |
||||
GPR_ASSERT(census_context_get_tag(context2, basic_tags[i].key, &tag) == 1); |
||||
GPR_ASSERT(compare_tag(&tag, &basic_tags[i])); |
||||
} |
||||
census_context_destroy(context); |
||||
census_context_destroy(context2); |
||||
} |
||||
|
||||
// replace a single tag value
|
||||
static void replace_value_test(void) { |
||||
struct census_context *context = |
||||
census_context_create(NULL, basic_tags, BASIC_TAG_COUNT, NULL); |
||||
const census_context_status *status; |
||||
struct census_context *context2 = census_context_create( |
||||
context, modify_tags + REPLACE_VALUE_OFFSET, 1, &status); |
||||
census_context_status expected = {4, 4, 0, 0, 1, 0, 0}; |
||||
GPR_ASSERT(memcmp(status, &expected, sizeof(expected)) == 0); |
||||
census_tag tag; |
||||
GPR_ASSERT(census_context_get_tag( |
||||
context2, modify_tags[REPLACE_VALUE_OFFSET].key, &tag) == 1); |
||||
GPR_ASSERT(compare_tag(&tag, &modify_tags[REPLACE_VALUE_OFFSET])); |
||||
census_context_destroy(context); |
||||
census_context_destroy(context2); |
||||
} |
||||
|
||||
// replace a single tags flags
|
||||
static void replace_flags_test(void) { |
||||
struct census_context *context = |
||||
census_context_create(NULL, basic_tags, BASIC_TAG_COUNT, NULL); |
||||
const census_context_status *status; |
||||
struct census_context *context2 = census_context_create( |
||||
context, modify_tags + REPLACE_FLAG_OFFSET, 1, &status); |
||||
census_context_status expected = {3, 5, 0, 0, 1, 0, 0}; |
||||
GPR_ASSERT(memcmp(status, &expected, sizeof(expected)) == 0); |
||||
census_tag tag; |
||||
GPR_ASSERT(census_context_get_tag( |
||||
context2, modify_tags[REPLACE_FLAG_OFFSET].key, &tag) == 1); |
||||
GPR_ASSERT(compare_tag(&tag, &modify_tags[REPLACE_FLAG_OFFSET])); |
||||
census_context_destroy(context); |
||||
census_context_destroy(context2); |
||||
} |
||||
|
||||
// delete a single tag.
|
||||
static void delete_tag_test(void) { |
||||
struct census_context *context = |
||||
census_context_create(NULL, basic_tags, BASIC_TAG_COUNT, NULL); |
||||
const census_context_status *status; |
||||
struct census_context *context2 = census_context_create( |
||||
context, modify_tags + DELETE_TAG_OFFSET, 1, &status); |
||||
census_context_status expected = {3, 4, 1, 0, 0, 0, 0}; |
||||
GPR_ASSERT(memcmp(status, &expected, sizeof(expected)) == 0); |
||||
census_tag tag; |
||||
GPR_ASSERT(census_context_get_tag( |
||||
context2, modify_tags[DELETE_TAG_OFFSET].key, &tag) == 0); |
||||
census_context_destroy(context); |
||||
census_context_destroy(context2); |
||||
} |
||||
|
||||
// add a single new tag.
|
||||
static void add_tag_test(void) { |
||||
struct census_context *context = |
||||
census_context_create(NULL, basic_tags, BASIC_TAG_COUNT, NULL); |
||||
const census_context_status *status; |
||||
struct census_context *context2 = |
||||
census_context_create(context, modify_tags + ADD_TAG_OFFSET, 1, &status); |
||||
census_context_status expected = {4, 5, 0, 1, 0, 0, 0}; |
||||
GPR_ASSERT(memcmp(status, &expected, sizeof(expected)) == 0); |
||||
census_tag tag; |
||||
GPR_ASSERT(census_context_get_tag(context2, modify_tags[ADD_TAG_OFFSET].key, |
||||
&tag) == 1); |
||||
GPR_ASSERT(compare_tag(&tag, &modify_tags[ADD_TAG_OFFSET])); |
||||
census_context_destroy(context); |
||||
census_context_destroy(context2); |
||||
} |
||||
|
||||
// test many changes at once.
|
||||
static void replace_add_delete_test(void) { |
||||
struct census_context *context = |
||||
census_context_create(NULL, basic_tags, BASIC_TAG_COUNT, NULL); |
||||
const census_context_status *status; |
||||
struct census_context *context2 = |
||||
census_context_create(context, modify_tags, MODIFY_TAG_COUNT, &status); |
||||
census_context_status expected = {3, 7, 1, 3, 4, 0, 0}; |
||||
GPR_ASSERT(memcmp(status, &expected, sizeof(expected)) == 0); |
||||
// validate context contents. Use specific indices into the two arrays
|
||||
// holding tag values.
|
||||
GPR_ASSERT(validate_tag(context2, &basic_tags[3])); |
||||
GPR_ASSERT(validate_tag(context2, &basic_tags[4])); |
||||
GPR_ASSERT(validate_tag(context2, &basic_tags[6])); |
||||
GPR_ASSERT(validate_tag(context2, &modify_tags[0])); |
||||
GPR_ASSERT(validate_tag(context2, &modify_tags[1])); |
||||
GPR_ASSERT(validate_tag(context2, &modify_tags[5])); |
||||
GPR_ASSERT(validate_tag(context2, &modify_tags[6])); |
||||
GPR_ASSERT(validate_tag(context2, &modify_tags[7])); |
||||
GPR_ASSERT(validate_tag(context2, &modify_tags[8])); |
||||
GPR_ASSERT(validate_tag(context2, &modify_tags[9])); |
||||
GPR_ASSERT(!validate_tag(context2, &basic_tags[0])); |
||||
GPR_ASSERT(!validate_tag(context2, &basic_tags[1])); |
||||
GPR_ASSERT(!validate_tag(context2, &basic_tags[2])); |
||||
GPR_ASSERT(!validate_tag(context2, &basic_tags[5])); |
||||
GPR_ASSERT(!validate_tag(context2, &basic_tags[7])); |
||||
census_context_destroy(context); |
||||
census_context_destroy(context2); |
||||
} |
||||
|
||||
#define BUF_SIZE 200 |
||||
|
||||
// test encode/decode.
|
||||
static void encode_decode_test(void) { |
||||
char buffer[BUF_SIZE]; |
||||
struct census_context *context = |
||||
census_context_create(NULL, basic_tags, BASIC_TAG_COUNT, NULL); |
||||
// Test with too small a buffer
|
||||
GPR_ASSERT(census_context_encode(context, buffer, 2) == 0); |
||||
// Test with sufficient buffer
|
||||
size_t buf_used = census_context_encode(context, buffer, BUF_SIZE); |
||||
GPR_ASSERT(buf_used != 0); |
||||
census_context *context2 = census_context_decode(buffer, buf_used); |
||||
GPR_ASSERT(context2 != NULL); |
||||
const census_context_status *status = census_context_get_status(context2); |
||||
census_context_status expected = {4, 0, 0, 0, 0, 0, 0}; |
||||
GPR_ASSERT(memcmp(status, &expected, sizeof(expected)) == 0); |
||||
for (int i = 0; i < BASIC_TAG_COUNT; i++) { |
||||
census_tag tag; |
||||
if (CENSUS_TAG_IS_PROPAGATED(basic_tags[i].flags)) { |
||||
GPR_ASSERT(census_context_get_tag(context2, basic_tags[i].key, &tag) == |
||||
1); |
||||
GPR_ASSERT(compare_tag(&tag, &basic_tags[i])); |
||||
} else { |
||||
GPR_ASSERT(census_context_get_tag(context2, basic_tags[i].key, &tag) == |
||||
0); |
||||
} |
||||
} |
||||
census_context_destroy(context2); |
||||
census_context_destroy(context); |
||||
} |
||||
|
||||
int main(int argc, char *argv[]) { |
||||
grpc_test_init(argc, argv); |
||||
empty_test(); |
||||
basic_test(); |
||||
lookup_by_key_test(); |
||||
invalid_test(); |
||||
copy_test(); |
||||
replace_value_test(); |
||||
replace_flags_test(); |
||||
delete_tag_test(); |
||||
add_tag_test(); |
||||
replace_add_delete_test(); |
||||
encode_decode_test(); |
||||
return 0; |
||||
} |
@ -1,284 +0,0 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2017 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/ext/census/intrusive_hash_map.h" |
||||
|
||||
#include <grpc/support/log.h> |
||||
#include <grpc/support/useful.h> |
||||
#include "test/core/util/test_config.h" |
||||
|
||||
#include <stdbool.h> |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
|
||||
/* The initial size of an intrusive hash map will be 2 to this power. */ |
||||
static const uint32_t kInitialLog2Size = 4; |
||||
|
||||
/* Simple object used for testing intrusive_hash_map. */ |
||||
typedef struct object { uint64_t val; } object; |
||||
|
||||
/* Helper function to allocate and initialize object. */ |
||||
static __inline object *make_new_object(uint64_t val) { |
||||
object *obj = (object *)gpr_malloc(sizeof(object)); |
||||
obj->val = val; |
||||
return obj; |
||||
} |
||||
|
||||
/* Wrapper struct for object. */ |
||||
typedef struct ptr_item { |
||||
INTRUSIVE_HASH_MAP_HEADER; |
||||
object *obj; |
||||
} ptr_item; |
||||
|
||||
/* Helper function that creates a new hash map item. It is up to the user to
|
||||
* free the item that was allocated. */ |
||||
static __inline ptr_item *make_ptr_item(uint64_t key, uint64_t value) { |
||||
ptr_item *new_item = (ptr_item *)gpr_malloc(sizeof(ptr_item)); |
||||
new_item->IHM_key = key; |
||||
new_item->IHM_hash_link = NULL; |
||||
new_item->obj = make_new_object(value); |
||||
return new_item; |
||||
} |
||||
|
||||
/* Helper function to deallocate ptr_item. */ |
||||
static void free_ptr_item(void *ptr) { gpr_free(((ptr_item *)ptr)->obj); } |
||||
|
||||
/* Simple string object used for testing intrusive_hash_map. */ |
||||
typedef struct string_item { |
||||
INTRUSIVE_HASH_MAP_HEADER; |
||||
// User data.
|
||||
char buf[32]; |
||||
uint16_t len; |
||||
} string_item; |
||||
|
||||
/* Helper function to allocate and initialize string object. */ |
||||
static string_item *make_string_item(uint64_t key, const char *buf, |
||||
uint16_t len) { |
||||
string_item *item = (string_item *)gpr_malloc(sizeof(string_item)); |
||||
item->IHM_key = key; |
||||
item->IHM_hash_link = NULL; |
||||
item->len = len; |
||||
memcpy(item->buf, buf, sizeof(char) * len); |
||||
return item; |
||||
} |
||||
|
||||
/* Helper function for comparing two string objects. */ |
||||
static bool compare_string_item(const string_item *A, const string_item *B) { |
||||
if (A->IHM_key != B->IHM_key || A->len != B->len) |
||||
return false; |
||||
else { |
||||
for (int i = 0; i < A->len; ++i) { |
||||
if (A->buf[i] != B->buf[i]) return false; |
||||
} |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
void test_empty() { |
||||
intrusive_hash_map hash_map; |
||||
intrusive_hash_map_init(&hash_map, kInitialLog2Size); |
||||
GPR_ASSERT(0 == intrusive_hash_map_size(&hash_map)); |
||||
GPR_ASSERT(intrusive_hash_map_empty(&hash_map)); |
||||
intrusive_hash_map_free(&hash_map, NULL); |
||||
} |
||||
|
||||
void test_single_item() { |
||||
intrusive_hash_map hash_map; |
||||
intrusive_hash_map_init(&hash_map, kInitialLog2Size); |
||||
|
||||
ptr_item *new_item = make_ptr_item(10, 20); |
||||
bool ok = intrusive_hash_map_insert(&hash_map, (hm_item *)new_item); |
||||
GPR_ASSERT(ok); |
||||
|
||||
ptr_item *item1 = |
||||
(ptr_item *)intrusive_hash_map_find(&hash_map, (uint64_t)10); |
||||
GPR_ASSERT(item1->obj->val == 20); |
||||
GPR_ASSERT(item1 == new_item); |
||||
|
||||
ptr_item *item2 = |
||||
(ptr_item *)intrusive_hash_map_erase(&hash_map, (uint64_t)10); |
||||
GPR_ASSERT(item2 == new_item); |
||||
|
||||
gpr_free(new_item->obj); |
||||
gpr_free(new_item); |
||||
GPR_ASSERT(0 == intrusive_hash_map_size(&hash_map)); |
||||
intrusive_hash_map_free(&hash_map, &free_ptr_item); |
||||
} |
||||
|
||||
void test_two_items() { |
||||
intrusive_hash_map hash_map; |
||||
intrusive_hash_map_init(&hash_map, kInitialLog2Size); |
||||
|
||||
string_item *new_item1 = make_string_item(10, "test1", 5); |
||||
bool ok = intrusive_hash_map_insert(&hash_map, (hm_item *)new_item1); |
||||
GPR_ASSERT(ok); |
||||
string_item *new_item2 = make_string_item(20, "test2", 5); |
||||
ok = intrusive_hash_map_insert(&hash_map, (hm_item *)new_item2); |
||||
GPR_ASSERT(ok); |
||||
|
||||
string_item *item1 = |
||||
(string_item *)intrusive_hash_map_find(&hash_map, (uint64_t)10); |
||||
GPR_ASSERT(compare_string_item(new_item1, item1)); |
||||
GPR_ASSERT(item1 == new_item1); |
||||
string_item *item2 = |
||||
(string_item *)intrusive_hash_map_find(&hash_map, (uint64_t)20); |
||||
GPR_ASSERT(compare_string_item(new_item2, item2)); |
||||
GPR_ASSERT(item2 == new_item2); |
||||
|
||||
item1 = (string_item *)intrusive_hash_map_erase(&hash_map, (uint64_t)10); |
||||
GPR_ASSERT(item1 == new_item1); |
||||
item2 = (string_item *)intrusive_hash_map_erase(&hash_map, (uint64_t)20); |
||||
GPR_ASSERT(item2 == new_item2); |
||||
|
||||
gpr_free(new_item1); |
||||
gpr_free(new_item2); |
||||
GPR_ASSERT(0 == intrusive_hash_map_size(&hash_map)); |
||||
intrusive_hash_map_free(&hash_map, NULL); |
||||
} |
||||
|
||||
// Test resetting and clearing the hash map.
|
||||
void test_reset_clear() { |
||||
intrusive_hash_map hash_map; |
||||
intrusive_hash_map_init(&hash_map, kInitialLog2Size); |
||||
|
||||
// Add some data to the hash_map.
|
||||
for (uint64_t i = 0; i < 3; ++i) { |
||||
intrusive_hash_map_insert(&hash_map, (hm_item *)make_ptr_item(i, i)); |
||||
} |
||||
GPR_ASSERT(3 == intrusive_hash_map_size(&hash_map)); |
||||
|
||||
// Test find.
|
||||
for (uint64_t i = 0; i < 3; ++i) { |
||||
ptr_item *item = (ptr_item *)intrusive_hash_map_find(&hash_map, i); |
||||
GPR_ASSERT(item != NULL); |
||||
GPR_ASSERT(item->IHM_key == i && item->obj->val == i); |
||||
} |
||||
|
||||
intrusive_hash_map_clear(&hash_map, &free_ptr_item); |
||||
GPR_ASSERT(intrusive_hash_map_empty(&hash_map)); |
||||
intrusive_hash_map_free(&hash_map, &free_ptr_item); |
||||
} |
||||
|
||||
// Check that the hash_map contains every key between [min_value, max_value]
|
||||
// (inclusive).
|
||||
void check_hash_map_values(intrusive_hash_map *hash_map, uint64_t min_value, |
||||
uint64_t max_value) { |
||||
GPR_ASSERT(intrusive_hash_map_size(hash_map) == max_value - min_value + 1); |
||||
|
||||
for (uint64_t i = min_value; i <= max_value; ++i) { |
||||
ptr_item *item = (ptr_item *)intrusive_hash_map_find(hash_map, i); |
||||
GPR_ASSERT(item != NULL); |
||||
GPR_ASSERT(item->obj->val == i); |
||||
} |
||||
} |
||||
|
||||
// Add many items and cause the hash_map to extend.
|
||||
void test_extend() { |
||||
intrusive_hash_map hash_map; |
||||
intrusive_hash_map_init(&hash_map, kInitialLog2Size); |
||||
|
||||
const uint64_t kNumValues = (1 << 16); |
||||
|
||||
for (uint64_t i = 0; i < kNumValues; ++i) { |
||||
ptr_item *item = make_ptr_item(i, i); |
||||
bool ok = intrusive_hash_map_insert(&hash_map, (hm_item *)item); |
||||
GPR_ASSERT(ok); |
||||
if (i % 1000 == 0) { |
||||
check_hash_map_values(&hash_map, 0, i); |
||||
} |
||||
} |
||||
|
||||
for (uint64_t i = 0; i < kNumValues; ++i) { |
||||
ptr_item *item = (ptr_item *)intrusive_hash_map_find(&hash_map, i); |
||||
GPR_ASSERT(item != NULL); |
||||
GPR_ASSERT(item->IHM_key == i && item->obj->val == i); |
||||
ptr_item *item2 = (ptr_item *)intrusive_hash_map_erase(&hash_map, i); |
||||
GPR_ASSERT(item == item2); |
||||
gpr_free(item->obj); |
||||
gpr_free(item); |
||||
} |
||||
|
||||
GPR_ASSERT(intrusive_hash_map_empty(&hash_map)); |
||||
intrusive_hash_map_free(&hash_map, &free_ptr_item); |
||||
} |
||||
|
||||
void test_stress() { |
||||
intrusive_hash_map hash_map; |
||||
intrusive_hash_map_init(&hash_map, kInitialLog2Size); |
||||
size_t n = 0; |
||||
|
||||
// Randomly add and insert entries 1000000 times.
|
||||
for (uint64_t i = 0; i < 1000000; ++i) { |
||||
int op = rand() & 0x1; |
||||
|
||||
switch (op) { |
||||
// Case 0 is insertion of entry.
|
||||
case 0: { |
||||
uint64_t key = (uint64_t)(rand() % 10000); |
||||
ptr_item *item = make_ptr_item(key, key); |
||||
bool ok = intrusive_hash_map_insert(&hash_map, (hm_item *)item); |
||||
if (ok) { |
||||
n++; |
||||
} else { |
||||
gpr_free(item->obj); |
||||
gpr_free(item); |
||||
} |
||||
break; |
||||
} |
||||
// Case 1 is removal of entry.
|
||||
case 1: { |
||||
uint64_t key = (uint64_t)(rand() % 10000); |
||||
ptr_item *item = (ptr_item *)intrusive_hash_map_find(&hash_map, key); |
||||
if (item != NULL) { |
||||
n--; |
||||
GPR_ASSERT(key == item->obj->val); |
||||
ptr_item *item2 = |
||||
(ptr_item *)intrusive_hash_map_erase(&hash_map, key); |
||||
GPR_ASSERT(item == item2); |
||||
gpr_free(item->obj); |
||||
gpr_free(item); |
||||
} |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
// Check size
|
||||
GPR_ASSERT(n == intrusive_hash_map_size(&hash_map)); |
||||
|
||||
// Clean the hash_map up.
|
||||
intrusive_hash_map_clear(&hash_map, &free_ptr_item); |
||||
GPR_ASSERT(intrusive_hash_map_empty(&hash_map)); |
||||
intrusive_hash_map_free(&hash_map, &free_ptr_item); |
||||
} |
||||
|
||||
int main(int argc, char **argv) { |
||||
grpc_test_init(argc, argv); |
||||
gpr_time_init(); |
||||
srand((unsigned)gpr_now(GPR_CLOCK_REALTIME).tv_nsec); |
||||
|
||||
test_empty(); |
||||
test_single_item(); |
||||
test_two_items(); |
||||
test_reset_clear(); |
||||
test_extend(); |
||||
test_stress(); |
||||
|
||||
return 0; |
||||
} |
@ -1,574 +0,0 @@ |
||||
/*
|
||||
* |
||||
* 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/ext/census/mlog.h" |
||||
#include <grpc/support/cpu.h> |
||||
#include <grpc/support/log.h> |
||||
#include <grpc/support/port_platform.h> |
||||
#include <grpc/support/sync.h> |
||||
#include <grpc/support/thd.h> |
||||
#include <grpc/support/time.h> |
||||
#include <grpc/support/useful.h> |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#include "test/core/util/test_config.h" |
||||
|
||||
// Change this to non-zero if you want more output.
|
||||
#define VERBOSE 0 |
||||
|
||||
// Log size to use for all tests.
|
||||
#define LOG_SIZE_IN_MB 1 |
||||
#define LOG_SIZE_IN_BYTES (LOG_SIZE_IN_MB << 20) |
||||
|
||||
// Fills in 'record' of size 'size'. Each byte in record is filled in with the
|
||||
// same value. The value is extracted from 'record' pointer.
|
||||
static void write_record(char* record, size_t size) { |
||||
char data = (char)((uintptr_t)record % 255); |
||||
memset(record, data, size); |
||||
} |
||||
|
||||
// Reads fixed size records. Returns the number of records read in
|
||||
// 'num_records'.
|
||||
static void read_records(size_t record_size, const char* buffer, |
||||
size_t buffer_size, int* num_records) { |
||||
GPR_ASSERT(buffer_size >= record_size); |
||||
GPR_ASSERT(buffer_size % record_size == 0); |
||||
*num_records = (int)(buffer_size / record_size); |
||||
for (int i = 0; i < *num_records; ++i) { |
||||
const char* record = buffer + (record_size * (size_t)i); |
||||
char data = (char)((uintptr_t)record % 255); |
||||
for (size_t j = 0; j < record_size; ++j) { |
||||
GPR_ASSERT(data == record[j]); |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Tries to write the specified number of records. Stops when the log gets
|
||||
// full. Returns the number of records written. Spins for random
|
||||
// number of times, up to 'max_spin_count', between writes.
|
||||
static int write_records_to_log(int writer_id, size_t record_size, |
||||
int num_records, int max_spin_count) { |
||||
int counter = 0; |
||||
for (int i = 0; i < num_records; ++i) { |
||||
int spin_count = max_spin_count ? rand() % max_spin_count : 0; |
||||
if (VERBOSE && (counter++ == num_records / 10)) { |
||||
printf(" Writer %d: %d out of %d written\n", writer_id, i, num_records); |
||||
counter = 0; |
||||
} |
||||
char* record = (char*)(census_log_start_write(record_size)); |
||||
if (record == NULL) { |
||||
return i; |
||||
} |
||||
write_record(record, record_size); |
||||
census_log_end_write(record, record_size); |
||||
for (int j = 0; j < spin_count; ++j) { |
||||
GPR_ASSERT(j >= 0); |
||||
} |
||||
} |
||||
return num_records; |
||||
} |
||||
|
||||
// Performs a single read iteration. Returns the number of records read.
|
||||
static int perform_read_iteration(size_t record_size) { |
||||
const void* read_buffer = NULL; |
||||
size_t bytes_available; |
||||
int records_read = 0; |
||||
census_log_init_reader(); |
||||
while ((read_buffer = census_log_read_next(&bytes_available))) { |
||||
int num_records = 0; |
||||
read_records(record_size, (const char*)read_buffer, bytes_available, |
||||
&num_records); |
||||
records_read += num_records; |
||||
} |
||||
return records_read; |
||||
} |
||||
|
||||
// Asserts that the log is empty.
|
||||
static void assert_log_empty(void) { |
||||
census_log_init_reader(); |
||||
size_t bytes_available; |
||||
GPR_ASSERT(census_log_read_next(&bytes_available) == NULL); |
||||
} |
||||
|
||||
// Fills the log and verifies data. If 'no fragmentation' is true, records
|
||||
// are sized such that CENSUS_LOG_2_MAX_RECORD_SIZE is a multiple of record
|
||||
// size. If not a circular log, verifies that the number of records written
|
||||
// match the number of records read.
|
||||
static void fill_log(size_t log_size, int no_fragmentation, int circular_log) { |
||||
size_t size; |
||||
if (no_fragmentation) { |
||||
int log2size = rand() % (CENSUS_LOG_2_MAX_RECORD_SIZE + 1); |
||||
size = ((size_t)1 << log2size); |
||||
} else { |
||||
while (1) { |
||||
size = 1 + ((size_t)rand() % CENSUS_LOG_MAX_RECORD_SIZE); |
||||
if (CENSUS_LOG_MAX_RECORD_SIZE % size) { |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
int records_written = |
||||
write_records_to_log(0 /* writer id */, size, |
||||
(int)((log_size / size) * 2), 0 /* spin count */); |
||||
int records_read = perform_read_iteration(size); |
||||
if (!circular_log) { |
||||
GPR_ASSERT(records_written == records_read); |
||||
} |
||||
assert_log_empty(); |
||||
} |
||||
|
||||
// Structure to pass args to writer_thread
|
||||
typedef struct writer_thread_args { |
||||
// Index of this thread in the writers vector.
|
||||
int index; |
||||
// Record size.
|
||||
size_t record_size; |
||||
// Number of records to write.
|
||||
int num_records; |
||||
// Used to signal when writer is complete
|
||||
gpr_cv* done; |
||||
gpr_mu* mu; |
||||
int* count; |
||||
} writer_thread_args; |
||||
|
||||
// Writes the given number of records of random size (up to kMaxRecordSize) and
|
||||
// random data to the specified log.
|
||||
static void writer_thread(void* arg) { |
||||
writer_thread_args* args = (writer_thread_args*)arg; |
||||
// Maximum number of times to spin between writes.
|
||||
static const int MAX_SPIN_COUNT = 50; |
||||
int records_written = 0; |
||||
if (VERBOSE) { |
||||
printf(" Writer %d starting\n", args->index); |
||||
} |
||||
while (records_written < args->num_records) { |
||||
records_written += write_records_to_log(args->index, args->record_size, |
||||
args->num_records - records_written, |
||||
MAX_SPIN_COUNT); |
||||
if (records_written < args->num_records) { |
||||
// Ran out of log space. Sleep for a bit and let the reader catch up.
|
||||
// This should never happen for circular logs.
|
||||
if (VERBOSE) { |
||||
printf( |
||||
" Writer %d stalled due to out-of-space: %d out of %d " |
||||
"written\n", |
||||
args->index, records_written, args->num_records); |
||||
} |
||||
gpr_sleep_until(grpc_timeout_milliseconds_to_deadline(10)); |
||||
} |
||||
} |
||||
// Done. Decrement count and signal.
|
||||
gpr_mu_lock(args->mu); |
||||
(*args->count)--; |
||||
gpr_cv_signal(args->done); |
||||
if (VERBOSE) { |
||||
printf(" Writer %d done\n", args->index); |
||||
} |
||||
gpr_mu_unlock(args->mu); |
||||
} |
||||
|
||||
// struct to pass args to reader_thread
|
||||
typedef struct reader_thread_args { |
||||
// Record size.
|
||||
size_t record_size; |
||||
// Interval between read iterations.
|
||||
int read_iteration_interval_in_msec; |
||||
// Total number of records.
|
||||
int total_records; |
||||
// Signalled when reader should stop.
|
||||
gpr_cv stop; |
||||
int stop_flag; |
||||
// Used to signal when reader has finished
|
||||
gpr_cv* done; |
||||
gpr_mu* mu; |
||||
int running; |
||||
} reader_thread_args; |
||||
|
||||
// Reads and verifies the specified number of records. Reader can also be
|
||||
// stopped via gpr_cv_signal(&args->stop). Sleeps for 'read_interval_in_msec'
|
||||
// between read iterations.
|
||||
static void reader_thread(void* arg) { |
||||
reader_thread_args* args = (reader_thread_args*)arg; |
||||
if (VERBOSE) { |
||||
printf(" Reader starting\n"); |
||||
} |
||||
gpr_timespec interval = gpr_time_from_micros( |
||||
args->read_iteration_interval_in_msec * 1000, GPR_TIMESPAN); |
||||
gpr_mu_lock(args->mu); |
||||
int records_read = 0; |
||||
int num_iterations = 0; |
||||
int counter = 0; |
||||
while (!args->stop_flag && records_read < args->total_records) { |
||||
gpr_cv_wait(&args->stop, args->mu, interval); |
||||
if (!args->stop_flag) { |
||||
records_read += perform_read_iteration(args->record_size); |
||||
GPR_ASSERT(records_read <= args->total_records); |
||||
if (VERBOSE && (counter++ == 100000)) { |
||||
printf(" Reader: %d out of %d read\n", records_read, |
||||
args->total_records); |
||||
counter = 0; |
||||
} |
||||
++num_iterations; |
||||
} |
||||
} |
||||
// Done
|
||||
args->running = 0; |
||||
gpr_cv_signal(args->done); |
||||
if (VERBOSE) { |
||||
printf(" Reader: records: %d, iterations: %d\n", records_read, |
||||
num_iterations); |
||||
} |
||||
gpr_mu_unlock(args->mu); |
||||
} |
||||
|
||||
// Creates NUM_WRITERS writers where each writer writes NUM_RECORDS_PER_WRITER
|
||||
// records. Also, starts a reader that iterates over and reads blocks every
|
||||
// READ_ITERATION_INTERVAL_IN_MSEC.
|
||||
// Number of writers.
|
||||
#define NUM_WRITERS 5 |
||||
static void multiple_writers_single_reader(int circular_log) { |
||||
// Sleep interval between read iterations.
|
||||
static const int READ_ITERATION_INTERVAL_IN_MSEC = 10; |
||||
// Maximum record size.
|
||||
static const size_t MAX_RECORD_SIZE = 20; |
||||
// Number of records written by each writer. This is sized such that we
|
||||
// will write through the entire log ~10 times.
|
||||
const int NUM_RECORDS_PER_WRITER = |
||||
(int)((10 * census_log_remaining_space()) / (MAX_RECORD_SIZE / 2)) / |
||||
NUM_WRITERS; |
||||
size_t record_size = ((size_t)rand() % MAX_RECORD_SIZE) + 1; |
||||
// Create and start writers.
|
||||
writer_thread_args writers[NUM_WRITERS]; |
||||
int writers_count = NUM_WRITERS; |
||||
gpr_cv writers_done; |
||||
gpr_mu writers_mu; // protects writers_done and writers_count
|
||||
gpr_cv_init(&writers_done); |
||||
gpr_mu_init(&writers_mu); |
||||
gpr_thd_id id; |
||||
for (int i = 0; i < NUM_WRITERS; ++i) { |
||||
writers[i].index = i; |
||||
writers[i].record_size = record_size; |
||||
writers[i].num_records = NUM_RECORDS_PER_WRITER; |
||||
writers[i].done = &writers_done; |
||||
writers[i].count = &writers_count; |
||||
writers[i].mu = &writers_mu; |
||||
gpr_thd_new(&id, &writer_thread, &writers[i], NULL); |
||||
} |
||||
// Start reader.
|
||||
gpr_cv reader_done; |
||||
gpr_mu reader_mu; // protects reader_done and reader.running
|
||||
reader_thread_args reader; |
||||
reader.record_size = record_size; |
||||
reader.read_iteration_interval_in_msec = READ_ITERATION_INTERVAL_IN_MSEC; |
||||
reader.total_records = NUM_WRITERS * NUM_RECORDS_PER_WRITER; |
||||
reader.stop_flag = 0; |
||||
gpr_cv_init(&reader.stop); |
||||
gpr_cv_init(&reader_done); |
||||
reader.done = &reader_done; |
||||
gpr_mu_init(&reader_mu); |
||||
reader.mu = &reader_mu; |
||||
reader.running = 1; |
||||
gpr_thd_new(&id, &reader_thread, &reader, NULL); |
||||
// Wait for writers to finish.
|
||||
gpr_mu_lock(&writers_mu); |
||||
while (writers_count != 0) { |
||||
gpr_cv_wait(&writers_done, &writers_mu, gpr_inf_future(GPR_CLOCK_REALTIME)); |
||||
} |
||||
gpr_mu_unlock(&writers_mu); |
||||
gpr_mu_destroy(&writers_mu); |
||||
gpr_cv_destroy(&writers_done); |
||||
gpr_mu_lock(&reader_mu); |
||||
if (circular_log) { |
||||
// Stop reader.
|
||||
reader.stop_flag = 1; |
||||
gpr_cv_signal(&reader.stop); |
||||
} |
||||
// wait for reader to finish
|
||||
while (reader.running) { |
||||
gpr_cv_wait(&reader_done, &reader_mu, gpr_inf_future(GPR_CLOCK_REALTIME)); |
||||
} |
||||
if (circular_log) { |
||||
// Assert that there were no out-of-space errors.
|
||||
GPR_ASSERT(0 == census_log_out_of_space_count()); |
||||
} |
||||
gpr_mu_unlock(&reader_mu); |
||||
gpr_mu_destroy(&reader_mu); |
||||
gpr_cv_destroy(&reader_done); |
||||
if (VERBOSE) { |
||||
printf(" Reader: finished\n"); |
||||
} |
||||
} |
||||
|
||||
static void setup_test(int circular_log) { |
||||
census_log_initialize(LOG_SIZE_IN_MB, circular_log); |
||||
// GPR_ASSERT(census_log_remaining_space() == LOG_SIZE_IN_BYTES);
|
||||
} |
||||
|
||||
// Attempts to create a record of invalid size (size >
|
||||
// CENSUS_LOG_MAX_RECORD_SIZE).
|
||||
void test_invalid_record_size(void) { |
||||
static const size_t INVALID_SIZE = CENSUS_LOG_MAX_RECORD_SIZE + 1; |
||||
static const size_t VALID_SIZE = 1; |
||||
printf("Starting test: invalid record size\n"); |
||||
setup_test(0); |
||||
void* record = census_log_start_write(INVALID_SIZE); |
||||
GPR_ASSERT(record == NULL); |
||||
// Now try writing a valid record.
|
||||
record = census_log_start_write(VALID_SIZE); |
||||
GPR_ASSERT(record != NULL); |
||||
census_log_end_write(record, VALID_SIZE); |
||||
// Verifies that available space went down by one block. In theory, this
|
||||
// check can fail if the thread is context switched to a new CPU during the
|
||||
// start_write execution (multiple blocks get allocated), but this has not
|
||||
// been observed in practice.
|
||||
// GPR_ASSERT(LOG_SIZE_IN_BYTES - CENSUS_LOG_MAX_RECORD_SIZE ==
|
||||
// census_log_remaining_space());
|
||||
census_log_shutdown(); |
||||
} |
||||
|
||||
// Tests end_write() with a different size than what was specified in
|
||||
// start_write().
|
||||
void test_end_write_with_different_size(void) { |
||||
static const size_t START_WRITE_SIZE = 10; |
||||
static const size_t END_WRITE_SIZE = 7; |
||||
printf("Starting test: end write with different size\n"); |
||||
setup_test(0); |
||||
void* record_written = census_log_start_write(START_WRITE_SIZE); |
||||
GPR_ASSERT(record_written != NULL); |
||||
census_log_end_write(record_written, END_WRITE_SIZE); |
||||
census_log_init_reader(); |
||||
size_t bytes_available; |
||||
const void* record_read = census_log_read_next(&bytes_available); |
||||
GPR_ASSERT(record_written == record_read); |
||||
GPR_ASSERT(END_WRITE_SIZE == bytes_available); |
||||
assert_log_empty(); |
||||
census_log_shutdown(); |
||||
} |
||||
|
||||
// Verifies that pending records are not available via read_next().
|
||||
void test_read_pending_record(void) { |
||||
static const size_t PR_RECORD_SIZE = 1024; |
||||
printf("Starting test: read pending record\n"); |
||||
setup_test(0); |
||||
// Start a write.
|
||||
void* record_written = census_log_start_write(PR_RECORD_SIZE); |
||||
GPR_ASSERT(record_written != NULL); |
||||
// As write is pending, read should fail.
|
||||
census_log_init_reader(); |
||||
size_t bytes_available; |
||||
const void* record_read = census_log_read_next(&bytes_available); |
||||
GPR_ASSERT(record_read == NULL); |
||||
// A read followed by end_write() should succeed.
|
||||
census_log_end_write(record_written, PR_RECORD_SIZE); |
||||
census_log_init_reader(); |
||||
record_read = census_log_read_next(&bytes_available); |
||||
GPR_ASSERT(record_written == record_read); |
||||
GPR_ASSERT(PR_RECORD_SIZE == bytes_available); |
||||
assert_log_empty(); |
||||
census_log_shutdown(); |
||||
} |
||||
|
||||
// Tries reading beyond pending write.
|
||||
void test_read_beyond_pending_record(void) { |
||||
printf("Starting test: read beyond pending record\n"); |
||||
setup_test(0); |
||||
// Start a write.
|
||||
const size_t incomplete_record_size = 10; |
||||
void* incomplete_record = census_log_start_write(incomplete_record_size); |
||||
GPR_ASSERT(incomplete_record != NULL); |
||||
const size_t complete_record_size = 20; |
||||
void* complete_record = census_log_start_write(complete_record_size); |
||||
GPR_ASSERT(complete_record != NULL); |
||||
GPR_ASSERT(complete_record != incomplete_record); |
||||
census_log_end_write(complete_record, complete_record_size); |
||||
// Now iterate over blocks to read completed records.
|
||||
census_log_init_reader(); |
||||
size_t bytes_available; |
||||
const void* record_read = census_log_read_next(&bytes_available); |
||||
GPR_ASSERT(complete_record == record_read); |
||||
GPR_ASSERT(complete_record_size == bytes_available); |
||||
// Complete first record.
|
||||
census_log_end_write(incomplete_record, incomplete_record_size); |
||||
// Have read past the incomplete record, so read_next() should return NULL.
|
||||
// NB: this test also assumes our thread did not get switched to a different
|
||||
// CPU between the two start_write calls
|
||||
record_read = census_log_read_next(&bytes_available); |
||||
GPR_ASSERT(record_read == NULL); |
||||
// Reset reader to get the newly completed record.
|
||||
census_log_init_reader(); |
||||
record_read = census_log_read_next(&bytes_available); |
||||
GPR_ASSERT(incomplete_record == record_read); |
||||
GPR_ASSERT(incomplete_record_size == bytes_available); |
||||
assert_log_empty(); |
||||
census_log_shutdown(); |
||||
} |
||||
|
||||
// Tests scenario where block being read is detached from a core and put on the
|
||||
// dirty list.
|
||||
void test_detached_while_reading(void) { |
||||
printf("Starting test: detached while reading\n"); |
||||
setup_test(0); |
||||
// Start a write.
|
||||
static const size_t DWR_RECORD_SIZE = 10; |
||||
void* record_written = census_log_start_write(DWR_RECORD_SIZE); |
||||
GPR_ASSERT(record_written != NULL); |
||||
census_log_end_write(record_written, DWR_RECORD_SIZE); |
||||
// Read this record.
|
||||
census_log_init_reader(); |
||||
size_t bytes_available; |
||||
const void* record_read = census_log_read_next(&bytes_available); |
||||
GPR_ASSERT(record_read != NULL); |
||||
GPR_ASSERT(DWR_RECORD_SIZE == bytes_available); |
||||
// Now fill the log. This will move the block being read from core-local
|
||||
// array to the dirty list.
|
||||
while ((record_written = census_log_start_write(DWR_RECORD_SIZE))) { |
||||
census_log_end_write(record_written, DWR_RECORD_SIZE); |
||||
} |
||||
|
||||
// In this iteration, read_next() should only traverse blocks in the
|
||||
// core-local array. Therefore, we expect at most gpr_cpu_num_cores() more
|
||||
// blocks. As log is full, if read_next() is traversing the dirty list, we
|
||||
// will get more than gpr_cpu_num_cores() blocks.
|
||||
int block_read = 0; |
||||
while ((record_read = census_log_read_next(&bytes_available))) { |
||||
++block_read; |
||||
GPR_ASSERT(block_read <= (int)gpr_cpu_num_cores()); |
||||
} |
||||
census_log_shutdown(); |
||||
} |
||||
|
||||
// Fills non-circular log with records sized such that size is a multiple of
|
||||
// CENSUS_LOG_MAX_RECORD_SIZE (no per-block fragmentation).
|
||||
void test_fill_log_no_fragmentation(void) { |
||||
printf("Starting test: fill log no fragmentation\n"); |
||||
const int circular = 0; |
||||
setup_test(circular); |
||||
fill_log(LOG_SIZE_IN_BYTES, 1 /* no fragmentation */, circular); |
||||
census_log_shutdown(); |
||||
} |
||||
|
||||
// Fills circular log with records sized such that size is a multiple of
|
||||
// CENSUS_LOG_MAX_RECORD_SIZE (no per-block fragmentation).
|
||||
void test_fill_circular_log_no_fragmentation(void) { |
||||
printf("Starting test: fill circular log no fragmentation\n"); |
||||
const int circular = 1; |
||||
setup_test(circular); |
||||
fill_log(LOG_SIZE_IN_BYTES, 1 /* no fragmentation */, circular); |
||||
census_log_shutdown(); |
||||
} |
||||
|
||||
// Fills non-circular log with records that may straddle end of a block.
|
||||
void test_fill_log_with_straddling_records(void) { |
||||
printf("Starting test: fill log with straddling records\n"); |
||||
const int circular = 0; |
||||
setup_test(circular); |
||||
fill_log(LOG_SIZE_IN_BYTES, 0 /* block straddling records */, circular); |
||||
census_log_shutdown(); |
||||
} |
||||
|
||||
// Fills circular log with records that may straddle end of a block.
|
||||
void test_fill_circular_log_with_straddling_records(void) { |
||||
printf("Starting test: fill circular log with straddling records\n"); |
||||
const int circular = 1; |
||||
setup_test(circular); |
||||
fill_log(LOG_SIZE_IN_BYTES, 0 /* block straddling records */, circular); |
||||
census_log_shutdown(); |
||||
} |
||||
|
||||
// Tests scenario where multiple writers and a single reader are using a log
|
||||
// that is configured to discard old records.
|
||||
void test_multiple_writers_circular_log(void) { |
||||
printf("Starting test: multiple writers circular log\n"); |
||||
const int circular = 1; |
||||
setup_test(circular); |
||||
multiple_writers_single_reader(circular); |
||||
census_log_shutdown(); |
||||
} |
||||
|
||||
// Tests scenario where multiple writers and a single reader are using a log
|
||||
// that is configured to discard old records.
|
||||
void test_multiple_writers(void) { |
||||
printf("Starting test: multiple writers\n"); |
||||
const int circular = 0; |
||||
setup_test(circular); |
||||
multiple_writers_single_reader(circular); |
||||
census_log_shutdown(); |
||||
} |
||||
|
||||
// Repeat the straddling records and multiple writers tests with a small log.
|
||||
void test_small_log(void) { |
||||
printf("Starting test: small log\n"); |
||||
const int circular = 0; |
||||
census_log_initialize(0, circular); |
||||
size_t log_size = census_log_remaining_space(); |
||||
GPR_ASSERT(log_size > 0); |
||||
fill_log(log_size, 0, circular); |
||||
census_log_shutdown(); |
||||
census_log_initialize(0, circular); |
||||
multiple_writers_single_reader(circular); |
||||
census_log_shutdown(); |
||||
} |
||||
|
||||
void test_performance(void) { |
||||
for (size_t write_size = 1; write_size < CENSUS_LOG_MAX_RECORD_SIZE; |
||||
write_size *= 2) { |
||||
setup_test(0); |
||||
gpr_timespec start_time = gpr_now(GPR_CLOCK_REALTIME); |
||||
int nrecords = 0; |
||||
while (1) { |
||||
void* record = census_log_start_write(write_size); |
||||
if (record == NULL) { |
||||
break; |
||||
} |
||||
census_log_end_write(record, write_size); |
||||
nrecords++; |
||||
} |
||||
gpr_timespec write_time = |
||||
gpr_time_sub(gpr_now(GPR_CLOCK_REALTIME), start_time); |
||||
double write_time_micro = |
||||
(double)write_time.tv_sec * 1000000 + (double)write_time.tv_nsec / 1000; |
||||
census_log_shutdown(); |
||||
printf( |
||||
"Wrote %d %d byte records in %.3g microseconds: %g records/us " |
||||
"(%g ns/record), %g gigabytes/s\n", |
||||
nrecords, (int)write_size, write_time_micro, |
||||
nrecords / write_time_micro, 1000 * write_time_micro / nrecords, |
||||
(double)((int)write_size * nrecords) / write_time_micro / 1000); |
||||
} |
||||
} |
||||
|
||||
int main(int argc, char** argv) { |
||||
grpc_test_init(argc, argv); |
||||
gpr_time_init(); |
||||
srand((unsigned)gpr_now(GPR_CLOCK_REALTIME).tv_nsec); |
||||
test_invalid_record_size(); |
||||
test_end_write_with_different_size(); |
||||
test_read_pending_record(); |
||||
test_read_beyond_pending_record(); |
||||
test_detached_while_reading(); |
||||
test_fill_log_no_fragmentation(); |
||||
test_fill_circular_log_no_fragmentation(); |
||||
test_fill_log_with_straddling_records(); |
||||
test_fill_circular_log_with_straddling_records(); |
||||
test_small_log(); |
||||
test_multiple_writers(); |
||||
test_multiple_writers_circular_log(); |
||||
test_performance(); |
||||
return 0; |
||||
} |
@ -1,154 +0,0 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2016 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/ext/census/resource.h" |
||||
#include <grpc/census.h> |
||||
#include <grpc/support/log.h> |
||||
#include <grpc/support/port_platform.h> |
||||
#include <grpc/support/useful.h> |
||||
#include <stdbool.h> |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include "src/core/ext/census/base_resources.h" |
||||
#include "test/core/util/test_config.h" |
||||
|
||||
// Test all the functionality for dealing with Resources.
|
||||
|
||||
// Just startup and shutdown resources subsystem.
|
||||
static void test_enable_disable() { |
||||
initialize_resources(); |
||||
shutdown_resources(); |
||||
} |
||||
|
||||
// A blank/empty initialization should not work.
|
||||
static void test_empty_definition() { |
||||
initialize_resources(); |
||||
int32_t rid = census_define_resource(NULL, 0); |
||||
GPR_ASSERT(rid == -1); |
||||
uint8_t buffer[50] = {0}; |
||||
rid = census_define_resource(buffer, 50); |
||||
GPR_ASSERT(rid == -1); |
||||
shutdown_resources(); |
||||
} |
||||
|
||||
// Given a file name, read raw proto and define the resource included within.
|
||||
// Returns resource id from census_define_resource().
|
||||
static int32_t define_resource_from_file(const char *file) { |
||||
#define BUF_SIZE 512 |
||||
uint8_t buffer[BUF_SIZE]; |
||||
FILE *input = fopen(file, "rb"); |
||||
GPR_ASSERT(input != NULL); |
||||
size_t nbytes = fread(buffer, 1, BUF_SIZE, input); |
||||
GPR_ASSERT(nbytes != 0 && nbytes < BUF_SIZE && feof(input) && !ferror(input)); |
||||
int32_t rid = census_define_resource(buffer, nbytes); |
||||
GPR_ASSERT(fclose(input) == 0); |
||||
return rid; |
||||
} |
||||
|
||||
// Test definition of a single resource, using a proto read from a file. The
|
||||
// `succeed` parameter indicates whether we expect the definition to succeed or
|
||||
// fail. `name` is used to check that the returned resource can be looked up by
|
||||
// name.
|
||||
static void test_define_single_resource(const char *file, const char *name, |
||||
bool succeed) { |
||||
gpr_log(GPR_INFO, "Test defining resource \"%s\"\n", name); |
||||
initialize_resources(); |
||||
int32_t rid = define_resource_from_file(file); |
||||
if (succeed) { |
||||
GPR_ASSERT(rid >= 0); |
||||
int32_t rid2 = census_resource_id(name); |
||||
GPR_ASSERT(rid == rid2); |
||||
} else { |
||||
GPR_ASSERT(rid < 0); |
||||
} |
||||
shutdown_resources(); |
||||
} |
||||
|
||||
// Try deleting various resources (both those that exist and those that don't).
|
||||
static void test_delete_resource(const char *minimal_good, const char *full) { |
||||
initialize_resources(); |
||||
// Try deleting resource before any are defined.
|
||||
census_delete_resource(0); |
||||
// Create and check a couple of resources.
|
||||
int32_t rid1 = define_resource_from_file(minimal_good); |
||||
int32_t rid2 = define_resource_from_file(full); |
||||
GPR_ASSERT(rid1 >= 0 && rid2 >= 0 && rid1 != rid2); |
||||
int32_t rid3 = census_resource_id("minimal_good"); |
||||
int32_t rid4 = census_resource_id("full_resource"); |
||||
GPR_ASSERT(rid1 == rid3 && rid2 == rid4); |
||||
// Try deleting non-existant resources.
|
||||
census_delete_resource(-1); |
||||
census_delete_resource(rid1 + rid2 + 1); |
||||
census_delete_resource(10000000); |
||||
// Delete one of the previously defined resources and check for deletion.
|
||||
census_delete_resource(rid1); |
||||
rid3 = census_resource_id("minimal_good"); |
||||
GPR_ASSERT(rid3 < 0); |
||||
// Check that re-adding works.
|
||||
rid1 = define_resource_from_file(minimal_good); |
||||
GPR_ASSERT(rid1 >= 0); |
||||
rid3 = census_resource_id("minimal_good"); |
||||
GPR_ASSERT(rid1 == rid3); |
||||
shutdown_resources(); |
||||
} |
||||
|
||||
// Test define base resources.
|
||||
static void test_base_resources() { |
||||
initialize_resources(); |
||||
define_base_resources(); |
||||
int32_t rid1 = census_resource_id("client_rpc_latency"); |
||||
int32_t rid2 = census_resource_id("server_rpc_latency"); |
||||
GPR_ASSERT(rid1 >= 0 && rid2 >= 0 && rid1 != rid2); |
||||
shutdown_resources(); |
||||
} |
||||
|
||||
int main(int argc, char **argv) { |
||||
const char *resource_empty_name_pb, *resource_full_pb, |
||||
*resource_minimal_good_pb, *resource_no_name_pb, |
||||
*resource_no_numerator_pb, *resource_no_unit_pb; |
||||
if (argc == 7) { |
||||
resource_empty_name_pb = argv[1]; |
||||
resource_full_pb = argv[2]; |
||||
resource_minimal_good_pb = argv[3]; |
||||
resource_no_name_pb = argv[4]; |
||||
resource_no_numerator_pb = argv[5]; |
||||
resource_no_unit_pb = argv[6]; |
||||
} else { |
||||
GPR_ASSERT(argc == 1); |
||||
resource_empty_name_pb = "test/core/census/data/resource_empty_name.pb"; |
||||
resource_full_pb = "test/core/census/data/resource_full.pb"; |
||||
resource_minimal_good_pb = "test/core/census/data/resource_minimal_good.pb"; |
||||
resource_no_name_pb = "test/core/census/data/resource_no_name.pb"; |
||||
resource_no_numerator_pb = "test/core/census/data/resource_no_numerator.pb"; |
||||
resource_no_unit_pb = "test/core/census/data/resource_no_unit.pb"; |
||||
} |
||||
grpc_test_init(argc, argv); |
||||
test_enable_disable(); |
||||
test_empty_definition(); |
||||
test_define_single_resource(resource_minimal_good_pb, "minimal_good", true); |
||||
test_define_single_resource(resource_full_pb, "full_resource", true); |
||||
test_define_single_resource(resource_no_name_pb, "resource_no_name", false); |
||||
test_define_single_resource(resource_no_numerator_pb, "resource_no_numerator", |
||||
false); |
||||
test_define_single_resource(resource_no_unit_pb, "resource_no_unit", false); |
||||
test_define_single_resource(resource_empty_name_pb, "resource_empty_name", |
||||
false); |
||||
test_delete_resource(resource_minimal_good_pb, resource_full_pb); |
||||
test_base_resources(); |
||||
return 0; |
||||
} |
@ -1,215 +0,0 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2016 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 <grpc/census.h> |
||||
#include <grpc/support/log.h> |
||||
#include <grpc/support/port_platform.h> |
||||
#include <grpc/support/useful.h> |
||||
#include <stdbool.h> |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include "src/core/ext/census/base_resources.h" |
||||
#include "src/core/ext/census/resource.h" |
||||
#include "test/core/util/test_config.h" |
||||
|
||||
#include "src/core/ext/census/gen/trace_context.pb.h" |
||||
#include "src/core/ext/census/trace_context.h" |
||||
#include "third_party/nanopb/pb_decode.h" |
||||
#include "third_party/nanopb/pb_encode.h" |
||||
|
||||
#define BUF_SIZE 256 |
||||
|
||||
/* Encodes a TraceContext structure (ctxt1) to a buffer, and then decodes it
|
||||
to a second TraceContext (ctxt2). Validates that the resulting TraceContext |
||||
has a span_id, trace_id, and that the values are equal to those in initial |
||||
TraceContext. On success, returns true. If encode_trace_context returns 0, |
||||
decode_trace_context fails, or the resulting TraceContext is missing a trace_id |
||||
or span_id, it will return false. */ |
||||
bool validate_encode_decode_context(google_trace_TraceContext *ctxt1, |
||||
uint8_t *buffer, size_t buf_size) { |
||||
google_trace_TraceContext ctxt2 = google_trace_TraceContext_init_zero; |
||||
size_t msg_length; |
||||
|
||||
msg_length = encode_trace_context(ctxt1, buffer, buf_size); |
||||
if (msg_length == 0) { |
||||
return false; |
||||
} |
||||
|
||||
if (!decode_trace_context(&ctxt2, buffer, msg_length)) { |
||||
return false; |
||||
} |
||||
|
||||
if (!ctxt2.has_trace_id_hi || !ctxt2.has_trace_id_lo || !ctxt2.has_span_id) { |
||||
return false; |
||||
} |
||||
|
||||
GPR_ASSERT(ctxt1->trace_id_hi == ctxt2.trace_id_hi && |
||||
ctxt1->trace_id_lo == ctxt2.trace_id_lo && |
||||
ctxt1->span_id == ctxt2.span_id && |
||||
ctxt1->has_span_options == ctxt2.has_span_options && |
||||
(ctxt1->has_span_options |
||||
? ctxt1->span_options == ctxt2.span_options |
||||
: true)); |
||||
|
||||
return true; |
||||
} |
||||
|
||||
/* Decodes a proto-encoded TraceContext from a buffer. If decode_trace_context
|
||||
fails or the resulting TraceContext is missing a trace_id or span_id it will |
||||
return false, otherwise returns true. */ |
||||
bool validate_decode_context(google_trace_TraceContext *ctxt, uint8_t *buffer, |
||||
size_t msg_length) { |
||||
// Validate the decoding of a context written to buffer.
|
||||
if (!decode_trace_context(ctxt, buffer, msg_length)) { |
||||
return false; |
||||
} |
||||
|
||||
if (!ctxt->has_trace_id_hi || !ctxt->has_trace_id_lo || !ctxt->has_span_id) { |
||||
return false; |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
/* Read an encoded trace context from a file. Validates that the decoding
|
||||
gives the expected result (succeed). */ |
||||
static void read_and_validate_context_from_file(google_trace_TraceContext *ctxt, |
||||
const char *file, |
||||
const bool succeed) { |
||||
uint8_t buffer[BUF_SIZE]; |
||||
FILE *input = fopen(file, "rb"); |
||||
GPR_ASSERT(input != NULL); |
||||
size_t nbytes = fread(buffer, 1, BUF_SIZE, input); |
||||
GPR_ASSERT(nbytes <= BUF_SIZE && feof(input) && !ferror(input)); |
||||
bool res = validate_decode_context(ctxt, buffer, nbytes); |
||||
GPR_ASSERT(res == succeed); |
||||
GPR_ASSERT(fclose(input) == 0); |
||||
} |
||||
|
||||
// Test full proto-buffer.
|
||||
static void test_full() { |
||||
google_trace_TraceContext ctxt = google_trace_TraceContext_init_zero; |
||||
read_and_validate_context_from_file( |
||||
&ctxt, "test/core/census/data/context_full.pb", true); |
||||
} |
||||
|
||||
// Test empty proto-buffer.
|
||||
static void test_empty() { |
||||
google_trace_TraceContext ctxt = google_trace_TraceContext_init_zero; |
||||
read_and_validate_context_from_file( |
||||
&ctxt, "test/core/census/data/context_empty.pb", false); |
||||
} |
||||
|
||||
// Test proto-buffer with only trace_id.
|
||||
static void test_trace_only() { |
||||
google_trace_TraceContext ctxt = google_trace_TraceContext_init_zero; |
||||
read_and_validate_context_from_file( |
||||
&ctxt, "test/core/census/data/context_trace_only.pb", false); |
||||
} |
||||
|
||||
// Test proto-buffer with only span_id.
|
||||
static void test_span_only() { |
||||
google_trace_TraceContext ctxt = google_trace_TraceContext_init_zero; |
||||
read_and_validate_context_from_file( |
||||
&ctxt, "test/core/census/data/context_span_only.pb", false); |
||||
} |
||||
|
||||
// Test proto-buffer without span_options value.
|
||||
static void test_no_span_options() { |
||||
google_trace_TraceContext ctxt = google_trace_TraceContext_init_zero; |
||||
read_and_validate_context_from_file( |
||||
&ctxt, "test/core/census/data/context_no_span_options.pb", true); |
||||
GPR_ASSERT(ctxt.has_span_options == false && ctxt.span_options == 0); |
||||
} |
||||
|
||||
static void test_encode_decode() { |
||||
uint8_t buffer[BUF_SIZE] = {0}; |
||||
|
||||
google_trace_TraceContext ctxt1 = google_trace_TraceContext_init_zero; |
||||
ctxt1.has_trace_id_hi = true; |
||||
ctxt1.has_trace_id_lo = true; |
||||
ctxt1.trace_id_lo = 1; |
||||
ctxt1.trace_id_hi = 2; |
||||
ctxt1.has_span_id = true; |
||||
ctxt1.span_id = 3; |
||||
validate_encode_decode_context(&ctxt1, buffer, sizeof(buffer)); |
||||
|
||||
// Missing trace_id. This should fail.
|
||||
google_trace_TraceContext ctxt2 = google_trace_TraceContext_init_zero; |
||||
ctxt2.has_trace_id_hi = false; |
||||
ctxt2.has_trace_id_lo = false; |
||||
ctxt2.has_span_id = true; |
||||
validate_encode_decode_context(&ctxt2, buffer, sizeof(buffer)); |
||||
} |
||||
|
||||
// Test a corrupted proto-buffer. This should fail.
|
||||
static void test_corrupt() { |
||||
uint8_t buffer[BUF_SIZE] = {0}; |
||||
google_trace_TraceContext ctxt1 = google_trace_TraceContext_init_zero; |
||||
size_t msg_length; |
||||
|
||||
ctxt1.has_trace_id_hi = true; |
||||
ctxt1.has_trace_id_lo = true; |
||||
ctxt1.trace_id_lo = 1; |
||||
ctxt1.trace_id_hi = 2; |
||||
ctxt1.has_span_id = true; |
||||
ctxt1.span_id = 3; |
||||
ctxt1.has_span_options = true; |
||||
ctxt1.span_options = SPAN_OPTIONS_IS_SAMPLED; |
||||
msg_length = encode_trace_context(&ctxt1, buffer, sizeof(buffer)); |
||||
|
||||
/* Corrupt some bytes. 255 (0xFF) should be illegal for the first byte of the
|
||||
proto encoded object. */ |
||||
buffer[0] = 255; |
||||
|
||||
bool res = validate_decode_context(&ctxt1, buffer, msg_length); |
||||
GPR_ASSERT(res == false); |
||||
} |
||||
|
||||
static void test_buffer_size() { |
||||
// This buffer is too small. This should fail.
|
||||
uint8_t buffer[16] = {0}; |
||||
google_trace_TraceContext ctxt1 = google_trace_TraceContext_init_zero; |
||||
size_t msg_length; |
||||
|
||||
ctxt1.has_trace_id_hi = true; |
||||
ctxt1.has_trace_id_lo = true; |
||||
ctxt1.trace_id_lo = 1; |
||||
ctxt1.trace_id_hi = 2; |
||||
ctxt1.has_span_id = true; |
||||
ctxt1.span_id = 3; |
||||
ctxt1.has_span_options = true; |
||||
ctxt1.span_options = SPAN_OPTIONS_IS_SAMPLED; |
||||
msg_length = encode_trace_context(&ctxt1, buffer, sizeof(buffer)); |
||||
|
||||
GPR_ASSERT(msg_length == 0); |
||||
} |
||||
|
||||
int main(int argc, char **argv) { |
||||
grpc_test_init(argc, argv); |
||||
test_full(); |
||||
test_empty(); |
||||
test_trace_only(); |
||||
test_span_only(); |
||||
test_encode_decode(); |
||||
test_corrupt(); |
||||
test_no_span_options(); |
||||
test_buffer_size(); |
||||
|
||||
return 0; |
||||
} |
Loading…
Reference in new issue