Remove test/core/census

pull/13147/head
Yash Tibrewal 7 years ago
parent d89c202dca
commit e92294c587
  1. 2
      test/core/bad_client/generate_tests.bzl
  2. 82
      test/core/census/BUILD
  3. 363
      test/core/census/context_test.cc
  4. 284
      test/core/census/intrusive_hash_map_test.cc
  5. 574
      test/core/census/mlog_test.cc
  6. 154
      test/core/census/resource_test.cc
  7. 215
      test/core/census/trace_context_test.cc

@ -40,7 +40,6 @@ def grpc_bad_client_tests():
name = 'bad_client_test',
srcs = ['bad_client.cc'],
hdrs = ['bad_client.h'],
copts = ['-std=c99'],
deps = ['//test/core/util:grpc_test_util', '//:grpc', '//:gpr', '//test/core/end2end:cq_verifier']
)
for t, topt in BAD_CLIENT_TESTS.items():
@ -48,6 +47,5 @@ def grpc_bad_client_tests():
name = '%s_bad_client_test' % t,
srcs = ['tests/%s.cc' % t],
deps = [':bad_client_test'],
copts = ['-std=c99'],
)

@ -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…
Cancel
Save