Index: third_party/grpc/test/core/iomgr/timer_heap_test.c |
diff --git a/third_party/grpc/test/core/iomgr/timer_heap_test.c b/third_party/grpc/test/core/iomgr/timer_heap_test.c |
new file mode 100644 |
index 0000000000000000000000000000000000000000..cd34696f7ddeb15d8be27a90d48e21bb3a316dfd |
--- /dev/null |
+++ b/third_party/grpc/test/core/iomgr/timer_heap_test.c |
@@ -0,0 +1,315 @@ |
+/* |
+ * |
+ * Copyright 2015-2016, Google Inc. |
+ * All rights reserved. |
+ * |
+ * Redistribution and use in source and binary forms, with or without |
+ * modification, are permitted provided that the following conditions are |
+ * met: |
+ * |
+ * * Redistributions of source code must retain the above copyright |
+ * notice, this list of conditions and the following disclaimer. |
+ * * Redistributions in binary form must reproduce the above |
+ * copyright notice, this list of conditions and the following disclaimer |
+ * in the documentation and/or other materials provided with the |
+ * distribution. |
+ * * Neither the name of Google Inc. nor the names of its |
+ * contributors may be used to endorse or promote products derived from |
+ * this software without specific prior written permission. |
+ * |
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
+ * |
+ */ |
+ |
+#include "src/core/iomgr/timer_heap.h" |
+ |
+#include <stdlib.h> |
+#include <string.h> |
+ |
+#include <grpc/support/alloc.h> |
+#include <grpc/support/log.h> |
+#include <grpc/support/useful.h> |
+ |
+#include "test/core/util/test_config.h" |
+ |
+static gpr_timespec random_deadline(void) { |
+ gpr_timespec ts; |
+ ts.tv_sec = rand(); |
+ ts.tv_nsec = rand(); |
+ ts.clock_type = GPR_CLOCK_REALTIME; |
+ return ts; |
+} |
+ |
+static grpc_timer *create_test_elements(size_t num_elements) { |
+ grpc_timer *elems = gpr_malloc(num_elements * sizeof(grpc_timer)); |
+ size_t i; |
+ for (i = 0; i < num_elements; i++) { |
+ elems[i].deadline = random_deadline(); |
+ } |
+ return elems; |
+} |
+ |
+static int contains(grpc_timer_heap *pq, grpc_timer *el) { |
+ size_t i; |
+ for (i = 0; i < pq->timer_count; i++) { |
+ if (pq->timers[i] == el) return 1; |
+ } |
+ return 0; |
+} |
+ |
+static void check_valid(grpc_timer_heap *pq) { |
+ size_t i; |
+ for (i = 0; i < pq->timer_count; ++i) { |
+ size_t left_child = 1u + 2u * i; |
+ size_t right_child = left_child + 1u; |
+ if (left_child < pq->timer_count) { |
+ GPR_ASSERT(gpr_time_cmp(pq->timers[i]->deadline, |
+ pq->timers[left_child]->deadline) <= 0); |
+ } |
+ if (right_child < pq->timer_count) { |
+ GPR_ASSERT(gpr_time_cmp(pq->timers[i]->deadline, |
+ pq->timers[right_child]->deadline) <= 0); |
+ } |
+ } |
+} |
+ |
+/******************************************************************************* |
+ * test1 |
+ */ |
+ |
+static void test1(void) { |
+ grpc_timer_heap pq; |
+ const size_t num_test_elements = 200; |
+ const size_t num_test_operations = 10000; |
+ size_t i; |
+ grpc_timer *test_elements = create_test_elements(num_test_elements); |
+ uint8_t *inpq = gpr_malloc(num_test_elements); |
+ |
+ gpr_log(GPR_INFO, "test1"); |
+ |
+ grpc_timer_heap_init(&pq); |
+ memset(inpq, 0, num_test_elements); |
+ GPR_ASSERT(grpc_timer_heap_is_empty(&pq)); |
+ check_valid(&pq); |
+ for (i = 0; i < num_test_elements; ++i) { |
+ GPR_ASSERT(!contains(&pq, &test_elements[i])); |
+ grpc_timer_heap_add(&pq, &test_elements[i]); |
+ check_valid(&pq); |
+ GPR_ASSERT(contains(&pq, &test_elements[i])); |
+ inpq[i] = 1; |
+ } |
+ for (i = 0; i < num_test_elements; ++i) { |
+ /* Test that check still succeeds even for element that wasn't just |
+ inserted. */ |
+ GPR_ASSERT(contains(&pq, &test_elements[i])); |
+ } |
+ |
+ GPR_ASSERT(pq.timer_count == num_test_elements); |
+ |
+ check_valid(&pq); |
+ |
+ for (i = 0; i < num_test_operations; ++i) { |
+ size_t elem_num = (size_t)rand() % num_test_elements; |
+ grpc_timer *el = &test_elements[elem_num]; |
+ if (!inpq[elem_num]) { /* not in pq */ |
+ GPR_ASSERT(!contains(&pq, el)); |
+ el->deadline = random_deadline(); |
+ grpc_timer_heap_add(&pq, el); |
+ GPR_ASSERT(contains(&pq, el)); |
+ inpq[elem_num] = 1; |
+ check_valid(&pq); |
+ } else { |
+ GPR_ASSERT(contains(&pq, el)); |
+ grpc_timer_heap_remove(&pq, el); |
+ GPR_ASSERT(!contains(&pq, el)); |
+ inpq[elem_num] = 0; |
+ check_valid(&pq); |
+ } |
+ } |
+ |
+ grpc_timer_heap_destroy(&pq); |
+ gpr_free(test_elements); |
+ gpr_free(inpq); |
+} |
+ |
+/******************************************************************************* |
+ * test2 |
+ */ |
+ |
+typedef struct { |
+ grpc_timer elem; |
+ bool inserted; |
+} elem_struct; |
+ |
+static elem_struct *search_elems(elem_struct *elems, size_t count, |
+ bool inserted) { |
+ size_t *search_order = gpr_malloc(count * sizeof(*search_order)); |
+ for (size_t i = 0; i < count; i++) { |
+ search_order[i] = i; |
+ } |
+ for (size_t i = 0; i < count * 2; i++) { |
+ size_t a = (size_t)rand() % count; |
+ size_t b = (size_t)rand() % count; |
+ GPR_SWAP(size_t, search_order[a], search_order[b]); |
+ } |
+ elem_struct *out = NULL; |
+ for (size_t i = 0; out == NULL && i < count; i++) { |
+ if (elems[search_order[i]].inserted == inserted) { |
+ out = &elems[search_order[i]]; |
+ } |
+ } |
+ gpr_free(search_order); |
+ return out; |
+} |
+ |
+static void test2(void) { |
+ gpr_log(GPR_INFO, "test2"); |
+ |
+ grpc_timer_heap pq; |
+ |
+ elem_struct elems[1000]; |
+ size_t num_inserted = 0; |
+ |
+ grpc_timer_heap_init(&pq); |
+ memset(elems, 0, sizeof(elems)); |
+ |
+ for (size_t round = 0; round < 10000; round++) { |
+ int r = rand() % 1000; |
+ if (r <= 550) { |
+ /* 55% of the time we try to add something */ |
+ elem_struct *el = search_elems(elems, GPR_ARRAY_SIZE(elems), false); |
+ if (el != NULL) { |
+ el->elem.deadline = random_deadline(); |
+ grpc_timer_heap_add(&pq, &el->elem); |
+ el->inserted = true; |
+ num_inserted++; |
+ check_valid(&pq); |
+ } |
+ } else if (r <= 650) { |
+ /* 10% of the time we try to remove something */ |
+ elem_struct *el = search_elems(elems, GPR_ARRAY_SIZE(elems), true); |
+ if (el != NULL) { |
+ grpc_timer_heap_remove(&pq, &el->elem); |
+ el->inserted = false; |
+ num_inserted--; |
+ check_valid(&pq); |
+ } |
+ } else { |
+ /* the remaining times we pop */ |
+ if (num_inserted > 0) { |
+ grpc_timer *top = grpc_timer_heap_top(&pq); |
+ grpc_timer_heap_pop(&pq); |
+ for (size_t i = 0; i < GPR_ARRAY_SIZE(elems); i++) { |
+ if (top == &elems[i].elem) { |
+ GPR_ASSERT(elems[i].inserted); |
+ elems[i].inserted = false; |
+ } |
+ } |
+ num_inserted--; |
+ check_valid(&pq); |
+ } |
+ } |
+ |
+ if (num_inserted) { |
+ gpr_timespec *min_deadline = NULL; |
+ for (size_t i = 0; i < GPR_ARRAY_SIZE(elems); i++) { |
+ if (elems[i].inserted) { |
+ if (min_deadline == NULL) { |
+ min_deadline = &elems[i].elem.deadline; |
+ } else { |
+ if (gpr_time_cmp(elems[i].elem.deadline, *min_deadline) < 0) { |
+ min_deadline = &elems[i].elem.deadline; |
+ } |
+ } |
+ } |
+ } |
+ GPR_ASSERT( |
+ 0 == gpr_time_cmp(grpc_timer_heap_top(&pq)->deadline, *min_deadline)); |
+ } |
+ } |
+ |
+ grpc_timer_heap_destroy(&pq); |
+} |
+ |
+static void shrink_test(void) { |
+ gpr_log(GPR_INFO, "shrink_test"); |
+ |
+ grpc_timer_heap pq; |
+ size_t i; |
+ size_t expected_size; |
+ |
+ /* A large random number to allow for multiple shrinkages, at least 512. */ |
+ const size_t num_elements = (size_t)rand() % 2000 + 512; |
+ |
+ grpc_timer_heap_init(&pq); |
+ |
+ /* Create a priority queue with many elements. Make sure the Size() is |
+ correct. */ |
+ for (i = 0; i < num_elements; ++i) { |
+ GPR_ASSERT(i == pq.timer_count); |
+ grpc_timer_heap_add(&pq, create_test_elements(1)); |
+ } |
+ GPR_ASSERT(num_elements == pq.timer_count); |
+ |
+ /* Remove elements until the Size is 1/4 the original size. */ |
+ while (pq.timer_count > num_elements / 4) { |
+ grpc_timer *const te = pq.timers[pq.timer_count - 1]; |
+ grpc_timer_heap_remove(&pq, te); |
+ gpr_free(te); |
+ } |
+ GPR_ASSERT(num_elements / 4 == pq.timer_count); |
+ |
+ /* Expect that Capacity is in the right range: |
+ Size * 2 <= Capacity <= Size * 4 */ |
+ GPR_ASSERT(pq.timer_count * 2 <= pq.timer_capacity); |
+ GPR_ASSERT(pq.timer_capacity <= pq.timer_count * 4); |
+ check_valid(&pq); |
+ |
+ /* Remove the rest of the elements. Check that the Capacity is not more than |
+ 4 times the Size and not less than 2 times, but never goes below 16. */ |
+ expected_size = pq.timer_count; |
+ while (pq.timer_count > 0) { |
+ const size_t which = (size_t)rand() % pq.timer_count; |
+ grpc_timer *te = pq.timers[which]; |
+ grpc_timer_heap_remove(&pq, te); |
+ gpr_free(te); |
+ expected_size--; |
+ GPR_ASSERT(expected_size == pq.timer_count); |
+ GPR_ASSERT(pq.timer_count * 2 <= pq.timer_capacity); |
+ if (pq.timer_count >= 8) { |
+ GPR_ASSERT(pq.timer_capacity <= pq.timer_count * 4); |
+ } else { |
+ GPR_ASSERT(16 <= pq.timer_capacity); |
+ } |
+ check_valid(&pq); |
+ } |
+ |
+ GPR_ASSERT(0 == pq.timer_count); |
+ GPR_ASSERT(pq.timer_capacity >= 16 && pq.timer_capacity < 32); |
+ |
+ grpc_timer_heap_destroy(&pq); |
+} |
+ |
+int main(int argc, char **argv) { |
+ int i; |
+ |
+ grpc_test_init(argc, argv); |
+ |
+ for (i = 0; i < 5; i++) { |
+ test1(); |
+ test2(); |
+ shrink_test(); |
+ } |
+ |
+ return 0; |
+} |