mirror of
https://github.com/vacp2p/boringssl.git
synced 2026-08-31 05:31:07 +00:00
This CL includes the result of running util/update_comment_style.py over all *.cc files in crypto/, and fixing omissions manually if necessary. Bug: 42290410 Change-Id: Iea86d8034c02ed9cef412c04a3af27ed6a6a6964 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/96151 Auto-Submit: Lily Chen <chlily@google.com> Commit-Queue: Lily Chen <chlily@google.com> SLSA-Policy-Verified: SLSA Policy Verification Service <devtools-gerritcodereview-exitgate@google.com> Reviewed-by: David Benjamin <davidben@google.com>
631 lines
20 KiB
C++
631 lines
20 KiB
C++
// 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);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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
|