| // Copyright 2018 The BoringSSL 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 | |
| // | |
| // https://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 <openssl/stack.h> | |
| #include <limits.h> | |
| #include <algorithm> | |
| #include <memory> | |
| #include <utility> | |
| #include <vector> | |
| #include <gtest/gtest.h> | |
| #include <openssl/err.h> | |
| #include <openssl/mem.h> | |
| #include <openssl/rand.h> | |
| #include "../mem_internal.h" | |
| // Define a custom stack type for testing. | |
| using TEST_INT = int; | |
| DEFINE_STACK_OF(TEST_INT) | |
| BSSL_NAMESPACE_BEGIN | |
| static void TEST_INT_free(TEST_INT *x) { Delete(x); } | |
| BORINGSSL_MAKE_DELETER(TEST_INT, TEST_INT_free) | |
| static UniquePtr<TEST_INT> TEST_INT_new(int x) { | |
| UniquePtr<TEST_INT> ret(New<TEST_INT>()); | |
| if (!ret) { | |
| return nullptr; | |
| } | |
| *ret = x; | |
| return ret; | |
| } | |
| namespace { | |
| struct ShallowStackDeleter { | |
| void operator()(STACK_OF(TEST_INT) *sk) const { sk_TEST_INT_free(sk); } | |
| }; | |
| using ShallowStack = std::unique_ptr<STACK_OF(TEST_INT), ShallowStackDeleter>; | |
| // kNull is treated as a nullptr expectation for purposes of ExpectStackEquals. | |
| // The tests in this file will never use it as a test value. | |
| static const int kNull = INT_MIN; | |
| static void ExpectStackEquals(const STACK_OF(TEST_INT) *sk, | |
| const std::vector<int> &vec) { | |
| EXPECT_EQ(vec.size(), sk_TEST_INT_num(sk)); | |
| for (size_t i = 0; i < vec.size(); i++) { | |
| SCOPED_TRACE(i); | |
| const TEST_INT *obj = sk_TEST_INT_value(sk, i); | |
| if (vec[i] == kNull) { | |
| EXPECT_FALSE(obj); | |
| } else { | |
| EXPECT_TRUE(obj); | |
| if (obj) { | |
| EXPECT_EQ(vec[i], *obj); | |
| } | |
| } | |
| } | |
| if (vec.empty()) { | |
| EXPECT_EQ(sk_TEST_INT_last(sk), nullptr); | |
| } else { | |
| const TEST_INT *obj = sk_TEST_INT_last(sk); | |
| ASSERT_TRUE(obj); | |
| EXPECT_EQ(vec.back(), *obj); | |
| } | |
| // Reading out-of-bounds fails. | |
| EXPECT_FALSE(sk_TEST_INT_value(sk, vec.size())); | |
| EXPECT_FALSE(sk_TEST_INT_value(sk, vec.size() + 1)); | |
| } | |
| TEST(StackTest, Basic) { | |
| UniquePtr<STACK_OF(TEST_INT)> sk(sk_TEST_INT_new_null()); | |
| ASSERT_TRUE(sk); | |
| // The stack starts out empty. | |
| ExpectStackEquals(sk.get(), {}); | |
| // Removing elements from an empty stack does nothing. | |
| EXPECT_FALSE(sk_TEST_INT_pop(sk.get())); | |
| EXPECT_FALSE(sk_TEST_INT_shift(sk.get())); | |
| EXPECT_FALSE(sk_TEST_INT_delete(sk.get(), 0)); | |
| // Push some elements. | |
| for (int i = 0; i < 6; i++) { | |
| auto value = TEST_INT_new(i); | |
| ASSERT_TRUE(value); | |
| ASSERT_TRUE(PushToStack(sk.get(), std::move(value))); | |
| } | |
| ExpectStackEquals(sk.get(), {0, 1, 2, 3, 4, 5}); | |
| // Items may be inserted in the middle. | |
| auto value = TEST_INT_new(6); | |
| ASSERT_TRUE(value); | |
| // Hold on to the object for later. | |
| TEST_INT *raw = value.get(); | |
| ASSERT_TRUE(sk_TEST_INT_insert(sk.get(), value.get(), 4)); | |
| value.release(); // sk_TEST_INT_insert takes ownership on success. | |
| ExpectStackEquals(sk.get(), {0, 1, 2, 3, 6, 4, 5}); | |
| // Without a comparison function, find searches by pointer. | |
| value = TEST_INT_new(6); | |
| ASSERT_TRUE(value); | |
| size_t index; | |
| EXPECT_FALSE(sk_TEST_INT_find(sk.get(), &index, value.get())); | |
| ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, raw)); | |
| EXPECT_EQ(4u, index); | |
| // sk_TEST_INT_insert can also insert values at the end. | |
| value = TEST_INT_new(7); | |
| ASSERT_TRUE(value); | |
| ASSERT_TRUE(sk_TEST_INT_insert(sk.get(), value.get(), 7)); | |
| value.release(); // sk_TEST_INT_insert takes ownership on success. | |
| ExpectStackEquals(sk.get(), {0, 1, 2, 3, 6, 4, 5, 7}); | |
| // Out-of-bounds indices are clamped. | |
| value = TEST_INT_new(8); | |
| ASSERT_TRUE(value); | |
| ASSERT_TRUE(sk_TEST_INT_insert(sk.get(), value.get(), 999)); | |
| value.release(); // sk_TEST_INT_insert takes ownership on success. | |
| ExpectStackEquals(sk.get(), {0, 1, 2, 3, 6, 4, 5, 7, 8}); | |
| // Test removing elements from various places. | |
| UniquePtr<TEST_INT> removed(sk_TEST_INT_pop(sk.get())); | |
| EXPECT_EQ(8, *removed); | |
| ExpectStackEquals(sk.get(), {0, 1, 2, 3, 6, 4, 5, 7}); | |
| removed.reset(sk_TEST_INT_shift(sk.get())); | |
| EXPECT_EQ(0, *removed); | |
| ExpectStackEquals(sk.get(), {1, 2, 3, 6, 4, 5, 7}); | |
| removed.reset(sk_TEST_INT_delete(sk.get(), 2)); | |
| EXPECT_EQ(3, *removed); | |
| ExpectStackEquals(sk.get(), {1, 2, 6, 4, 5, 7}); | |
| // Objects may also be deleted by pointer. | |
| removed.reset(sk_TEST_INT_delete_ptr(sk.get(), raw)); | |
| EXPECT_EQ(raw, removed.get()); | |
| ExpectStackEquals(sk.get(), {1, 2, 4, 5, 7}); | |
| // Deleting is a no-op is the object is not found. | |
| value = TEST_INT_new(100); | |
| ASSERT_TRUE(value); | |
| EXPECT_FALSE(sk_TEST_INT_delete_ptr(sk.get(), value.get())); | |
| // Insert nullptr to test deep copy handling of it. | |
| ASSERT_TRUE(sk_TEST_INT_insert(sk.get(), nullptr, 0)); | |
| ExpectStackEquals(sk.get(), {kNull, 1, 2, 4, 5, 7}); | |
| // Test both deep and shallow copies. | |
| UniquePtr<STACK_OF(TEST_INT)> copy(sk_TEST_INT_deep_copy( | |
| sk.get(), | |
| [](const TEST_INT *x) -> TEST_INT * { | |
| return x == nullptr ? nullptr : TEST_INT_new(*x).release(); | |
| }, | |
| TEST_INT_free)); | |
| ASSERT_TRUE(copy); | |
| ExpectStackEquals(copy.get(), {kNull, 1, 2, 4, 5, 7}); | |
| ShallowStack shallow(sk_TEST_INT_dup(sk.get())); | |
| ASSERT_TRUE(shallow); | |
| ASSERT_EQ(sk_TEST_INT_num(sk.get()), sk_TEST_INT_num(shallow.get())); | |
| for (size_t i = 0; i < sk_TEST_INT_num(sk.get()); i++) { | |
| EXPECT_EQ(sk_TEST_INT_value(sk.get(), i), | |
| sk_TEST_INT_value(shallow.get(), i)); | |
| } | |
| // Deep copies may fail. This should clean up temporaries. | |
| EXPECT_FALSE(sk_TEST_INT_deep_copy( | |
| sk.get(), | |
| [](const TEST_INT *x) -> TEST_INT * { | |
| return x == nullptr || *x == 4 ? nullptr : TEST_INT_new(*x).release(); | |
| }, | |
| TEST_INT_free)); | |
| // sk_TEST_INT_zero clears a stack, but does not free the elements. | |
| ShallowStack shallow2(sk_TEST_INT_dup(sk.get())); | |
| ASSERT_TRUE(shallow2); | |
| sk_TEST_INT_zero(shallow2.get()); | |
| ExpectStackEquals(shallow2.get(), {}); | |
| } | |
| TEST(StackTest, BigStack) { | |
| UniquePtr<STACK_OF(TEST_INT)> sk(sk_TEST_INT_new_null()); | |
| ASSERT_TRUE(sk); | |
| std::vector<int> expected; | |
| static const int kCount = 100000; | |
| for (int i = 0; i < kCount; i++) { | |
| auto value = TEST_INT_new(i); | |
| ASSERT_TRUE(value); | |
| ASSERT_TRUE(PushToStack(sk.get(), std::move(value))); | |
| expected.push_back(i); | |
| } | |
| ExpectStackEquals(sk.get(), expected); | |
| } | |
| static uint64_t g_compare_count = 0; | |
| static int compare(const TEST_INT *const *a, const TEST_INT *const *b) { | |
| g_compare_count++; | |
| if (**a < **b) { | |
| return -1; | |
| } | |
| if (**a > **b) { | |
| return 1; | |
| } | |
| return 0; | |
| } | |
| static int compare_reverse(const TEST_INT *const *a, const TEST_INT *const *b) { | |
| return -compare(a, b); | |
| } | |
| TEST(StackTest, Sorted) { | |
| std::vector<int> vec_sorted = {0, 1, 2, 3, 4, 5, 6}; | |
| std::vector<int> vec = vec_sorted; | |
| do { | |
| UniquePtr<STACK_OF(TEST_INT)> sk(sk_TEST_INT_new(compare)); | |
| ASSERT_TRUE(sk); | |
| for (int v : vec) { | |
| auto value = TEST_INT_new(v); | |
| ASSERT_TRUE(value); | |
| ASSERT_TRUE(PushToStack(sk.get(), std::move(value))); | |
| } | |
| // The stack is not (known to be) sorted. | |
| EXPECT_FALSE(sk_TEST_INT_is_sorted(sk.get())); | |
| // With a comparison function, find matches by value. | |
| auto ten = TEST_INT_new(10); | |
| ASSERT_TRUE(ten); | |
| size_t index; | |
| EXPECT_FALSE(sk_TEST_INT_find(sk.get(), &index, ten.get())); | |
| auto three = TEST_INT_new(3); | |
| ASSERT_TRUE(three); | |
| ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, three.get())); | |
| EXPECT_EQ(3, *sk_TEST_INT_value(sk.get(), index)); | |
| sk_TEST_INT_sort(sk.get()); | |
| EXPECT_TRUE(sk_TEST_INT_is_sorted(sk.get())); | |
| ExpectStackEquals(sk.get(), vec_sorted); | |
| // Sorting an already-sorted list is a no-op. | |
| uint64_t old_compare_count = g_compare_count; | |
| sk_TEST_INT_sort(sk.get()); | |
| EXPECT_EQ(old_compare_count, g_compare_count); | |
| EXPECT_TRUE(sk_TEST_INT_is_sorted(sk.get())); | |
| ExpectStackEquals(sk.get(), vec_sorted); | |
| // When sorted, find uses binary search. | |
| ASSERT_TRUE(ten); | |
| EXPECT_FALSE(sk_TEST_INT_find(sk.get(), &index, ten.get())); | |
| ASSERT_TRUE(three); | |
| ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, three.get())); | |
| EXPECT_EQ(3u, index); | |
| // Copies preserve comparison and sorted information. | |
| UniquePtr<STACK_OF(TEST_INT)> copy(sk_TEST_INT_deep_copy( | |
| sk.get(), | |
| [](const TEST_INT *x) -> TEST_INT * { | |
| return TEST_INT_new(*x).release(); | |
| }, | |
| TEST_INT_free)); | |
| ASSERT_TRUE(copy); | |
| EXPECT_TRUE(sk_TEST_INT_is_sorted(copy.get())); | |
| ASSERT_TRUE(sk_TEST_INT_find(copy.get(), &index, three.get())); | |
| EXPECT_EQ(3u, index); | |
| ShallowStack copy2(sk_TEST_INT_dup(sk.get())); | |
| ASSERT_TRUE(copy2); | |
| EXPECT_TRUE(sk_TEST_INT_is_sorted(copy2.get())); | |
| ASSERT_TRUE(sk_TEST_INT_find(copy2.get(), &index, three.get())); | |
| EXPECT_EQ(3u, index); | |
| // Removing elements does not affect sortedness. | |
| TEST_INT_free(sk_TEST_INT_delete(sk.get(), 0)); | |
| EXPECT_TRUE(sk_TEST_INT_is_sorted(sk.get())); | |
| EXPECT_TRUE(sk_TEST_INT_is_sorted(sk.get())); | |
| // Changing the comparison function invalidates sortedness. | |
| sk_TEST_INT_set_cmp_func(sk.get(), compare_reverse); | |
| EXPECT_FALSE(sk_TEST_INT_is_sorted(sk.get())); | |
| ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, three.get())); | |
| EXPECT_EQ(2u, index); | |
| sk_TEST_INT_sort(sk.get()); | |
| EXPECT_TRUE(sk_TEST_INT_is_sorted(sk.get())); | |
| ExpectStackEquals(sk.get(), {6, 5, 4, 3, 2, 1}); | |
| ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, three.get())); | |
| EXPECT_EQ(3u, index); | |
| // Inserting a new element invalidates sortedness. | |
| auto tmp = TEST_INT_new(10); | |
| ASSERT_TRUE(tmp); | |
| ASSERT_TRUE(PushToStack(sk.get(), std::move(tmp))); | |
| EXPECT_FALSE(sk_TEST_INT_is_sorted(sk.get())); | |
| ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, ten.get())); | |
| EXPECT_EQ(6u, index); | |
| } while (std::next_permutation(vec.begin(), vec.end())); | |
| } | |
| // sk_*_find should return the first matching element in all cases. | |
| TEST(StackTest, FindFirst) { | |
| UniquePtr<STACK_OF(TEST_INT)> sk(sk_TEST_INT_new(compare)); | |
| ASSERT_TRUE(sk); | |
| auto value = TEST_INT_new(1); | |
| ASSERT_TRUE(value); | |
| ASSERT_TRUE(PushToStack(sk.get(), std::move(value))); | |
| for (int i = 0; i < 10; i++) { | |
| value = TEST_INT_new(2); | |
| ASSERT_TRUE(value); | |
| ASSERT_TRUE(PushToStack(sk.get(), std::move(value))); | |
| } | |
| const TEST_INT *two = sk_TEST_INT_value(sk.get(), 1); | |
| // Pointer-based equality. | |
| size_t index; | |
| ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, two)); | |
| EXPECT_EQ(1u, index); | |
| // Comparator-based equality, unsorted. | |
| sk_TEST_INT_set_cmp_func(sk.get(), compare); | |
| EXPECT_FALSE(sk_TEST_INT_is_sorted(sk.get())); | |
| ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, two)); | |
| EXPECT_EQ(1u, index); | |
| // Comparator-based equality, sorted. | |
| sk_TEST_INT_sort(sk.get()); | |
| EXPECT_TRUE(sk_TEST_INT_is_sorted(sk.get())); | |
| ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, two)); | |
| EXPECT_EQ(1u, index); | |
| // Comparator-based equality, sorted and at the front. | |
| sk_TEST_INT_set_cmp_func(sk.get(), compare_reverse); | |
| sk_TEST_INT_sort(sk.get()); | |
| EXPECT_TRUE(sk_TEST_INT_is_sorted(sk.get())); | |
| ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, two)); | |
| EXPECT_EQ(0u, index); | |
| } | |
| // Exhaustively test the binary search. | |
| TEST(StackTest, BinarySearch) { | |
| static const size_t kCount = 100; | |
| for (size_t i = 0; i < kCount; i++) { | |
| SCOPED_TRACE(i); | |
| for (size_t j = i; j <= kCount; j++) { | |
| SCOPED_TRACE(j); | |
| // Make a stack where [0, i) are below, [i, j) match, and [j, kCount) are | |
| // above. | |
| UniquePtr<STACK_OF(TEST_INT)> sk(sk_TEST_INT_new(compare)); | |
| ASSERT_TRUE(sk); | |
| for (size_t k = 0; k < i; k++) { | |
| auto value = TEST_INT_new(-1); | |
| ASSERT_TRUE(value); | |
| ASSERT_TRUE(PushToStack(sk.get(), std::move(value))); | |
| } | |
| for (size_t k = i; k < j; k++) { | |
| auto value = TEST_INT_new(0); | |
| ASSERT_TRUE(value); | |
| ASSERT_TRUE(PushToStack(sk.get(), std::move(value))); | |
| } | |
| for (size_t k = j; k < kCount; k++) { | |
| auto value = TEST_INT_new(1); | |
| ASSERT_TRUE(value); | |
| ASSERT_TRUE(PushToStack(sk.get(), std::move(value))); | |
| } | |
| sk_TEST_INT_sort(sk.get()); | |
| auto key = TEST_INT_new(0); | |
| ASSERT_TRUE(key); | |
| size_t idx; | |
| int found = sk_TEST_INT_find(sk.get(), &idx, key.get()); | |
| if (i == j) { | |
| EXPECT_FALSE(found); | |
| } else { | |
| ASSERT_TRUE(found); | |
| EXPECT_EQ(i, idx); | |
| } | |
| } | |
| } | |
| } | |
| TEST(StackTest, DeleteIf) { | |
| UniquePtr<STACK_OF(TEST_INT)> sk(sk_TEST_INT_new(compare)); | |
| ASSERT_TRUE(sk); | |
| for (int v : {1, 9, 2, 8, 3, 7, 4, 6, 5}) { | |
| auto obj = TEST_INT_new(v); | |
| ASSERT_TRUE(obj); | |
| ASSERT_TRUE(PushToStack(sk.get(), std::move(obj))); | |
| } | |
| auto keep_only_multiples = [](TEST_INT *x, void *data) { | |
| auto d = static_cast<const int *>(data); | |
| if (*x % *d == 0) { | |
| return 0; | |
| } | |
| TEST_INT_free(x); | |
| return 1; | |
| }; | |
| int d = 2; | |
| sk_TEST_INT_delete_if(sk.get(), keep_only_multiples, &d); | |
| ExpectStackEquals(sk.get(), {2, 8, 4, 6}); | |
| EXPECT_FALSE(sk_TEST_INT_is_sorted(sk.get())); | |
| sk_TEST_INT_sort(sk.get()); | |
| ExpectStackEquals(sk.get(), {2, 4, 6, 8}); | |
| EXPECT_TRUE(sk_TEST_INT_is_sorted(sk.get())); | |
| // Keep only multiples of four. | |
| d = 4; | |
| sk_TEST_INT_delete_if(sk.get(), keep_only_multiples, &d); | |
| ExpectStackEquals(sk.get(), {4, 8}); | |
| // Removing elements preserves the sorted bit. | |
| EXPECT_TRUE(sk_TEST_INT_is_sorted(sk.get())); | |
| // Delete everything. | |
| d = 16; | |
| sk_TEST_INT_delete_if(sk.get(), keep_only_multiples, &d); | |
| ExpectStackEquals(sk.get(), {}); | |
| EXPECT_TRUE(sk_TEST_INT_is_sorted(sk.get())); | |
| } | |
| TEST(StackTest, SortAndDedup) { | |
| UniquePtr<STACK_OF(TEST_INT)> sk(sk_TEST_INT_new(compare)); | |
| ASSERT_TRUE(sk); | |
| sk_TEST_INT_sort_and_dedup(sk.get(), TEST_INT_free); | |
| ExpectStackEquals(sk.get(), std::vector<int>{}); | |
| for (int v : {1, 2, 3, 4, 5, 2, 2, 2, 2, 3}) { | |
| auto obj = TEST_INT_new(v); | |
| ASSERT_TRUE(obj); | |
| ASSERT_TRUE(PushToStack(sk.get(), std::move(obj))); | |
| } | |
| sk_TEST_INT_sort_and_dedup(sk.get(), TEST_INT_free); | |
| ExpectStackEquals(sk.get(), {1, 2, 3, 4, 5}); | |
| sk_TEST_INT_sort_and_dedup(sk.get(), TEST_INT_free); | |
| ExpectStackEquals(sk.get(), {1, 2, 3, 4, 5}); | |
| // Repeat with non-owning pointers. | |
| sk.reset(sk_TEST_INT_new(compare)); | |
| ASSERT_TRUE(sk); | |
| for (int v : {1, 2, 3, 4, 5, 2, 2, 2, 2, 3}) { | |
| auto obj = TEST_INT_new(v); | |
| ASSERT_TRUE(obj); | |
| ASSERT_TRUE(PushToStack(sk.get(), std::move(obj))); | |
| } | |
| // `not_owned` does not own its contents, so we cannot use bssl::UniquePtr. | |
| STACK_OF(TEST_INT) *not_owned = sk_TEST_INT_dup(sk.get()); | |
| ASSERT_TRUE(not_owned); | |
| sk_TEST_INT_sort_and_dedup(not_owned, nullptr); | |
| ExpectStackEquals(not_owned, {1, 2, 3, 4, 5}); | |
| sk_TEST_INT_sort_and_dedup(not_owned, nullptr); | |
| ExpectStackEquals(not_owned, {1, 2, 3, 4, 5}); | |
| sk_TEST_INT_free(not_owned); | |
| } | |
| TEST(StackTest, IsSorted) { | |
| UniquePtr<STACK_OF(TEST_INT)> sk(sk_TEST_INT_new_null()); | |
| ASSERT_TRUE(sk); | |
| EXPECT_FALSE(sk_TEST_INT_is_sorted(sk.get())); | |
| // Empty lists are always known to be sorted. | |
| sk_TEST_INT_set_cmp_func(sk.get(), compare); | |
| EXPECT_TRUE(sk_TEST_INT_is_sorted(sk.get())); | |
| // As are one-element lists. | |
| auto value = TEST_INT_new(2); | |
| ASSERT_TRUE(value); | |
| ASSERT_TRUE(PushToStack(sk.get(), std::move(value))); | |
| EXPECT_TRUE(sk_TEST_INT_is_sorted(sk.get())); | |
| // Two-element lists require an explicit sort. | |
| value = TEST_INT_new(1); | |
| ASSERT_TRUE(value); | |
| ASSERT_TRUE(PushToStack(sk.get(), std::move(value))); | |
| EXPECT_FALSE(sk_TEST_INT_is_sorted(sk.get())); | |
| // The list is now sorted. | |
| sk_TEST_INT_sort(sk.get()); | |
| EXPECT_TRUE(sk_TEST_INT_is_sorted(sk.get())); | |
| // After changing the comparison function, it no longer is sorted. | |
| sk_TEST_INT_set_cmp_func(sk.get(), compare_reverse); | |
| EXPECT_FALSE(sk_TEST_INT_is_sorted(sk.get())); | |
| sk_TEST_INT_sort(sk.get()); | |
| EXPECT_TRUE(sk_TEST_INT_is_sorted(sk.get())); | |
| // After changing one element, it is no longer sorted. | |
| value = TEST_INT_new(10); | |
| ASSERT_TRUE(value); | |
| TEST_INT_free(sk_TEST_INT_value(sk.get(), 0)); | |
| sk_TEST_INT_set(sk.get(), 0, value.release()); | |
| EXPECT_FALSE(sk_TEST_INT_is_sorted(sk.get())); | |
| sk_TEST_INT_sort(sk.get()); | |
| EXPECT_TRUE(sk_TEST_INT_is_sorted(sk.get())); | |
| // But, starting from one element, switching the comparison function preserves | |
| // the sorted bit. | |
| TEST_INT_free(sk_TEST_INT_pop(sk.get())); | |
| EXPECT_TRUE(sk_TEST_INT_is_sorted(sk.get())); | |
| sk_TEST_INT_set_cmp_func(sk.get(), compare); | |
| EXPECT_TRUE(sk_TEST_INT_is_sorted(sk.get())); | |
| value = TEST_INT_new(100); | |
| ASSERT_TRUE(value); | |
| TEST_INT_free(sk_TEST_INT_value(sk.get(), 0)); | |
| sk_TEST_INT_set(sk.get(), 0, value.release()); | |
| EXPECT_TRUE(sk_TEST_INT_is_sorted(sk.get())); | |
| // Without a comparison function, the list cannot be sorted. | |
| sk_TEST_INT_set_cmp_func(sk.get(), nullptr); | |
| EXPECT_FALSE(sk_TEST_INT_is_sorted(sk.get())); | |
| } | |
| TEST(StackTest, Sort) { | |
| constexpr size_t kMaxLength = 100; | |
| constexpr int kIterations = 500; | |
| for (size_t len = 0; len < kMaxLength; len++) { | |
| SCOPED_TRACE(len); | |
| for (int iter = 0; iter < kIterations; iter++) { | |
| // Make a random input list. | |
| std::vector<int> vec(len); | |
| RAND_bytes(reinterpret_cast<uint8_t *>(vec.data()), | |
| sizeof(int) * vec.size()); | |
| SCOPED_TRACE(testing::PrintToString(vec)); | |
| // Convert it to a `STACK_OF(TEST_INT)`. | |
| UniquePtr<STACK_OF(TEST_INT)> sk(sk_TEST_INT_new(compare)); | |
| ASSERT_TRUE(sk); | |
| for (int v : vec) { | |
| auto value = TEST_INT_new(v); | |
| ASSERT_TRUE(value); | |
| ASSERT_TRUE(PushToStack(sk.get(), std::move(value))); | |
| } | |
| // Sort it with our sort implementation. | |
| sk_TEST_INT_sort(sk.get()); | |
| std::vector<int> result; | |
| for (const TEST_INT *v : sk.get()) { | |
| result.push_back(*v); | |
| } | |
| // The result must match the STL's version. | |
| std::sort(vec.begin(), vec.end()); | |
| EXPECT_EQ(vec, result); | |
| } | |
| } | |
| } | |
| // STACK_OF(T) APIs should generally cleanly treat nullptr as the empty list. | |
| TEST(StackTest, NullIsEmpty) { | |
| // Clear the error queue, to be sampled later. | |
| ERR_clear_error(); | |
| EXPECT_EQ(0u, sk_TEST_INT_num(nullptr)); | |
| EXPECT_EQ(nullptr, sk_TEST_INT_value(nullptr, 0)); | |
| EXPECT_EQ(nullptr, sk_TEST_INT_value(nullptr, 1)); | |
| UniquePtr<TEST_INT> value = TEST_INT_new(6); | |
| ASSERT_TRUE(value); | |
| size_t index; | |
| EXPECT_FALSE(sk_TEST_INT_find(nullptr, &index, value.get())); | |
| // Deleting from an empty list is a no-op. | |
| EXPECT_EQ(nullptr, sk_TEST_INT_pop(nullptr)); | |
| EXPECT_EQ(nullptr, sk_TEST_INT_shift(nullptr)); | |
| EXPECT_EQ(nullptr, sk_TEST_INT_delete(nullptr, 0)); | |
| EXPECT_EQ(nullptr, sk_TEST_INT_delete(nullptr, 1)); | |
| EXPECT_EQ(nullptr, sk_TEST_INT_delete_ptr(nullptr, value.get())); | |
| sk_TEST_INT_delete_if( | |
| nullptr, | |
| [](TEST_INT *, void *) -> int { | |
| ADD_FAILURE() << "callback should not have been called"; | |
| return 0; | |
| }, | |
| nullptr); | |
| // An empty list is always sorted. | |
| EXPECT_TRUE(sk_TEST_INT_is_sorted(nullptr)); | |
| sk_TEST_INT_sort(nullptr); | |
| // Copying an empty list gives an empty list. | |
| EXPECT_EQ(nullptr, sk_TEST_INT_dup(nullptr)); | |
| EXPECT_EQ(nullptr, | |
| sk_TEST_INT_deep_copy( | |
| nullptr, | |
| [](const TEST_INT *) -> TEST_INT * { | |
| ADD_FAILURE() << "copy callback should not have been called"; | |
| return nullptr; | |
| }, | |
| [](TEST_INT *) { | |
| ADD_FAILURE() << "free callback should not have been called"; | |
| })); | |
| // None of these operations should have added to the error queue. | |
| EXPECT_EQ(0u, ERR_get_error()); | |
| } | |
| } // namespace | |
| BSSL_NAMESPACE_END |