mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-12-17 17:05:31 +00:00
Add TypedInteger
This CL adds a TypedInteger helper which provides additional type
safety in Dawn. It is a compile-time restriction that prevents integers
of different types from being used interchangably in Debug builds.
It also adds ityp::{array,bitset,span} as helper classes to wrap std::
versions (not span). These accept a template paramter as the Index type
so that typed integers, or enum classes, may be used as a type-safe
index.
For now, bind group layout binding indices use TypedInteger. Future
CLs will convert other indices to be type-safe as well.
Bug: dawn:442
Change-Id: I5b63b1e4f6154322db0227a7788a4e9b8303410e
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/19902
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Austin Eng <enga@chromium.org>
This commit is contained in:
committed by
Commit Bot service account
parent
3f4f356611
commit
7a4685f448
@@ -159,6 +159,9 @@ test("dawn_unittests") {
|
||||
"unittests/ErrorTests.cpp",
|
||||
"unittests/ExtensionTests.cpp",
|
||||
"unittests/GetProcAddressTests.cpp",
|
||||
"unittests/ITypArrayTests.cpp",
|
||||
"unittests/ITypBitsetTests.cpp",
|
||||
"unittests/ITypSpanTests.cpp",
|
||||
"unittests/LinkedListTests.cpp",
|
||||
"unittests/MathTests.cpp",
|
||||
"unittests/ObjectBaseTests.cpp",
|
||||
@@ -172,6 +175,7 @@ test("dawn_unittests") {
|
||||
"unittests/SlabAllocatorTests.cpp",
|
||||
"unittests/SystemUtilsTests.cpp",
|
||||
"unittests/ToBackendTests.cpp",
|
||||
"unittests/TypedIntegerTests.cpp",
|
||||
"unittests/validation/BindGroupValidationTests.cpp",
|
||||
"unittests/validation/BufferValidationTests.cpp",
|
||||
"unittests/validation/CommandBufferValidationTests.cpp",
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "common/BitSetIterator.h"
|
||||
#include "common/ityp_bitset.h"
|
||||
|
||||
// This is ANGLE's BitSetIterator_unittests.cpp file.
|
||||
|
||||
@@ -48,7 +49,7 @@ TEST_F(BitSetIteratorTest, Iterator) {
|
||||
// Test an empty iterator.
|
||||
TEST_F(BitSetIteratorTest, EmptySet) {
|
||||
// We don't use the FAIL gtest macro here since it returns immediately,
|
||||
// causing an unreachable code warning in MSVS
|
||||
// causing an unreachable code warning in MSVC
|
||||
bool sawBit = false;
|
||||
for (unsigned long bit : IterateBitSet(mStateBits)) {
|
||||
DAWN_UNUSED(bit);
|
||||
@@ -82,3 +83,137 @@ TEST_F(BitSetIteratorTest, NonLValueBitset) {
|
||||
|
||||
EXPECT_EQ((mStateBits & otherBits).count(), seenBits.size());
|
||||
}
|
||||
|
||||
class EnumBitSetIteratorTest : public testing::Test {
|
||||
protected:
|
||||
enum class TestEnum { A, B, C, D, E, F, G, H, I, J, EnumCount };
|
||||
|
||||
static constexpr size_t kEnumCount = static_cast<size_t>(TestEnum::EnumCount);
|
||||
ityp::bitset<TestEnum, kEnumCount> mStateBits;
|
||||
};
|
||||
|
||||
// Simple iterator test.
|
||||
TEST_F(EnumBitSetIteratorTest, Iterator) {
|
||||
std::set<TestEnum> originalValues;
|
||||
originalValues.insert(TestEnum::B);
|
||||
originalValues.insert(TestEnum::F);
|
||||
originalValues.insert(TestEnum::C);
|
||||
originalValues.insert(TestEnum::I);
|
||||
|
||||
for (TestEnum value : originalValues) {
|
||||
mStateBits.set(value);
|
||||
}
|
||||
|
||||
std::set<TestEnum> readValues;
|
||||
for (TestEnum bit : IterateBitSet(mStateBits)) {
|
||||
EXPECT_EQ(1u, originalValues.count(bit));
|
||||
EXPECT_EQ(0u, readValues.count(bit));
|
||||
readValues.insert(bit);
|
||||
}
|
||||
|
||||
EXPECT_EQ(originalValues.size(), readValues.size());
|
||||
}
|
||||
|
||||
// Test an empty iterator.
|
||||
TEST_F(EnumBitSetIteratorTest, EmptySet) {
|
||||
// We don't use the FAIL gtest macro here since it returns immediately,
|
||||
// causing an unreachable code warning in MSVC
|
||||
bool sawBit = false;
|
||||
for (TestEnum bit : IterateBitSet(mStateBits)) {
|
||||
DAWN_UNUSED(bit);
|
||||
sawBit = true;
|
||||
}
|
||||
EXPECT_FALSE(sawBit);
|
||||
}
|
||||
|
||||
// Test iterating a result of combining two bitsets.
|
||||
TEST_F(EnumBitSetIteratorTest, NonLValueBitset) {
|
||||
ityp::bitset<TestEnum, kEnumCount> otherBits;
|
||||
|
||||
mStateBits.set(TestEnum::B);
|
||||
mStateBits.set(TestEnum::C);
|
||||
mStateBits.set(TestEnum::D);
|
||||
mStateBits.set(TestEnum::E);
|
||||
|
||||
otherBits.set(TestEnum::A);
|
||||
otherBits.set(TestEnum::B);
|
||||
otherBits.set(TestEnum::D);
|
||||
otherBits.set(TestEnum::F);
|
||||
|
||||
std::set<TestEnum> seenBits;
|
||||
|
||||
for (TestEnum bit : IterateBitSet(mStateBits & otherBits)) {
|
||||
EXPECT_EQ(0u, seenBits.count(bit));
|
||||
seenBits.insert(bit);
|
||||
EXPECT_TRUE(mStateBits[bit]);
|
||||
EXPECT_TRUE(otherBits[bit]);
|
||||
}
|
||||
|
||||
EXPECT_EQ((mStateBits & otherBits).count(), seenBits.size());
|
||||
}
|
||||
|
||||
class ITypBitsetIteratorTest : public testing::Test {
|
||||
protected:
|
||||
using IntegerT = TypedInteger<struct Foo, uint32_t>;
|
||||
ityp::bitset<IntegerT, 40> mStateBits;
|
||||
};
|
||||
|
||||
// Simple iterator test.
|
||||
TEST_F(ITypBitsetIteratorTest, Iterator) {
|
||||
std::set<IntegerT> originalValues;
|
||||
originalValues.insert(IntegerT(2));
|
||||
originalValues.insert(IntegerT(6));
|
||||
originalValues.insert(IntegerT(8));
|
||||
originalValues.insert(IntegerT(35));
|
||||
|
||||
for (IntegerT value : originalValues) {
|
||||
mStateBits.set(value);
|
||||
}
|
||||
|
||||
std::set<IntegerT> readValues;
|
||||
for (IntegerT bit : IterateBitSet(mStateBits)) {
|
||||
EXPECT_EQ(1u, originalValues.count(bit));
|
||||
EXPECT_EQ(0u, readValues.count(bit));
|
||||
readValues.insert(bit);
|
||||
}
|
||||
|
||||
EXPECT_EQ(originalValues.size(), readValues.size());
|
||||
}
|
||||
|
||||
// Test an empty iterator.
|
||||
TEST_F(ITypBitsetIteratorTest, EmptySet) {
|
||||
// We don't use the FAIL gtest macro here since it returns immediately,
|
||||
// causing an unreachable code warning in MSVC
|
||||
bool sawBit = false;
|
||||
for (IntegerT bit : IterateBitSet(mStateBits)) {
|
||||
DAWN_UNUSED(bit);
|
||||
sawBit = true;
|
||||
}
|
||||
EXPECT_FALSE(sawBit);
|
||||
}
|
||||
|
||||
// Test iterating a result of combining two bitsets.
|
||||
TEST_F(ITypBitsetIteratorTest, NonLValueBitset) {
|
||||
ityp::bitset<IntegerT, 40> otherBits;
|
||||
|
||||
mStateBits.set(IntegerT(1));
|
||||
mStateBits.set(IntegerT(2));
|
||||
mStateBits.set(IntegerT(3));
|
||||
mStateBits.set(IntegerT(4));
|
||||
|
||||
otherBits.set(IntegerT(0));
|
||||
otherBits.set(IntegerT(1));
|
||||
otherBits.set(IntegerT(3));
|
||||
otherBits.set(IntegerT(5));
|
||||
|
||||
std::set<IntegerT> seenBits;
|
||||
|
||||
for (IntegerT bit : IterateBitSet(mStateBits & otherBits)) {
|
||||
EXPECT_EQ(0u, seenBits.count(bit));
|
||||
seenBits.insert(bit);
|
||||
EXPECT_TRUE(mStateBits[bit]);
|
||||
EXPECT_TRUE(otherBits[bit]);
|
||||
}
|
||||
|
||||
EXPECT_EQ((mStateBits & otherBits).count(), seenBits.size());
|
||||
}
|
||||
|
||||
95
src/tests/unittests/ITypArrayTests.cpp
Normal file
95
src/tests/unittests/ITypArrayTests.cpp
Normal file
@@ -0,0 +1,95 @@
|
||||
// Copyright 2020 The Dawn 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 <gtest/gtest.h>
|
||||
|
||||
#include "common/TypedInteger.h"
|
||||
#include "common/ityp_array.h"
|
||||
|
||||
class ITypArrayTest : public testing::Test {
|
||||
protected:
|
||||
using Key = TypedInteger<struct KeyT, uint32_t>;
|
||||
using Val = TypedInteger<struct ValT, uint32_t>;
|
||||
using Array = ityp::array<Key, Val, 10>;
|
||||
|
||||
// Test that the expected array methods can be constexpr
|
||||
struct ConstexprTest {
|
||||
static constexpr Array kArr = {Val(0), Val(1), Val(2), Val(3), Val(4),
|
||||
Val(5), Val(6), Val(7), Val(8), Val(9)};
|
||||
|
||||
static_assert(kArr[Key(3)] == Val(3), "");
|
||||
static_assert(kArr.at(Key(7)) == Val(7), "");
|
||||
static_assert(kArr.size() == Key(10), "");
|
||||
};
|
||||
};
|
||||
|
||||
// Test that values can be set at an index and retrieved from the same index.
|
||||
TEST_F(ITypArrayTest, Indexing) {
|
||||
Array arr;
|
||||
{
|
||||
arr[Key(2)] = Val(5);
|
||||
arr[Key(1)] = Val(9);
|
||||
arr[Key(9)] = Val(2);
|
||||
|
||||
ASSERT_EQ(arr[Key(2)], Val(5));
|
||||
ASSERT_EQ(arr[Key(1)], Val(9));
|
||||
ASSERT_EQ(arr[Key(9)], Val(2));
|
||||
}
|
||||
{
|
||||
arr.at(Key(4)) = Val(5);
|
||||
arr.at(Key(3)) = Val(8);
|
||||
arr.at(Key(1)) = Val(7);
|
||||
|
||||
ASSERT_EQ(arr.at(Key(4)), Val(5));
|
||||
ASSERT_EQ(arr.at(Key(3)), Val(8));
|
||||
ASSERT_EQ(arr.at(Key(1)), Val(7));
|
||||
}
|
||||
}
|
||||
|
||||
// Test that the array can be iterated in order with a range-based for loop
|
||||
TEST_F(ITypArrayTest, RangeBasedIteration) {
|
||||
Array arr;
|
||||
|
||||
// Assign in a non-const range-based for loop
|
||||
uint32_t i = 0;
|
||||
for (Val& val : arr) {
|
||||
val = Val(i);
|
||||
}
|
||||
|
||||
// Check values in a const range-based for loop
|
||||
i = 0;
|
||||
for (Val val : static_cast<const Array&>(arr)) {
|
||||
ASSERT_EQ(val, arr[Key(i++)]);
|
||||
}
|
||||
}
|
||||
|
||||
// Test that begin/end/front/back/data return pointers/references to the correct elements.
|
||||
TEST_F(ITypArrayTest, BeginEndFrontBackData) {
|
||||
Array arr;
|
||||
|
||||
// non-const versions
|
||||
ASSERT_EQ(arr.begin(), &arr[Key(0)]);
|
||||
ASSERT_EQ(arr.end(), &arr[Key(0)] + static_cast<uint32_t>(arr.size()));
|
||||
ASSERT_EQ(&arr.front(), &arr[Key(0)]);
|
||||
ASSERT_EQ(&arr.back(), &arr[Key(9)]);
|
||||
ASSERT_EQ(arr.data(), &arr[Key(0)]);
|
||||
|
||||
// const versions
|
||||
const Array& constArr = arr;
|
||||
ASSERT_EQ(constArr.begin(), &constArr[Key(0)]);
|
||||
ASSERT_EQ(constArr.end(), &constArr[Key(0)] + static_cast<uint32_t>(constArr.size()));
|
||||
ASSERT_EQ(&constArr.front(), &constArr[Key(0)]);
|
||||
ASSERT_EQ(&constArr.back(), &constArr[Key(9)]);
|
||||
ASSERT_EQ(constArr.data(), &constArr[Key(0)]);
|
||||
}
|
||||
178
src/tests/unittests/ITypBitsetTests.cpp
Normal file
178
src/tests/unittests/ITypBitsetTests.cpp
Normal file
@@ -0,0 +1,178 @@
|
||||
// Copyright 2020 The Dawn 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 <gtest/gtest.h>
|
||||
|
||||
#include "common/TypedInteger.h"
|
||||
#include "common/ityp_bitset.h"
|
||||
|
||||
#include <set>
|
||||
|
||||
class ITypBitsetTest : public testing::Test {
|
||||
protected:
|
||||
using Key = TypedInteger<struct KeyT, size_t>;
|
||||
using Bitset = ityp::bitset<Key, 9>;
|
||||
|
||||
// Test that the expected bitset methods can be constexpr
|
||||
struct ConstexprTest {
|
||||
static constexpr Bitset kBitset = {1 << 0 | 1 << 3 | 1 << 7 | 1 << 8};
|
||||
|
||||
static_assert(kBitset[Key(0)] == true, "");
|
||||
static_assert(kBitset[Key(1)] == false, "");
|
||||
static_assert(kBitset[Key(2)] == false, "");
|
||||
static_assert(kBitset[Key(3)] == true, "");
|
||||
static_assert(kBitset[Key(4)] == false, "");
|
||||
static_assert(kBitset[Key(5)] == false, "");
|
||||
static_assert(kBitset[Key(6)] == false, "");
|
||||
static_assert(kBitset[Key(7)] == true, "");
|
||||
static_assert(kBitset[Key(8)] == true, "");
|
||||
|
||||
static_assert(kBitset.size() == 9, "");
|
||||
};
|
||||
|
||||
void ExpectBits(const Bitset& bits, std::set<size_t> indices) {
|
||||
size_t mask = 0;
|
||||
|
||||
for (size_t i = 0; i < bits.size(); ++i) {
|
||||
if (indices.count(i) == 0) {
|
||||
ASSERT_FALSE(bits[Key(i)]) << i;
|
||||
ASSERT_FALSE(bits.test(Key(i))) << i;
|
||||
} else {
|
||||
mask |= (1 << i);
|
||||
ASSERT_TRUE(bits[Key(i)]) << i;
|
||||
ASSERT_TRUE(bits.test(Key(i))) << i;
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_EQ(bits.to_ullong(), mask);
|
||||
ASSERT_EQ(bits.to_ulong(), mask);
|
||||
ASSERT_EQ(bits.count(), indices.size());
|
||||
ASSERT_EQ(bits.all(), indices.size() == bits.size());
|
||||
ASSERT_EQ(bits.any(), indices.size() != 0);
|
||||
ASSERT_EQ(bits.none(), indices.size() == 0);
|
||||
}
|
||||
};
|
||||
|
||||
// Test that by default no bits are set
|
||||
TEST_F(ITypBitsetTest, DefaultZero) {
|
||||
Bitset bits;
|
||||
ExpectBits(bits, {});
|
||||
}
|
||||
|
||||
// Test the bitset can be initialized with a bitmask
|
||||
TEST_F(ITypBitsetTest, InitializeByBits) {
|
||||
Bitset bits = {1 << 1 | 1 << 2 | 1 << 7};
|
||||
ExpectBits(bits, {1, 2, 7});
|
||||
}
|
||||
|
||||
// Test that bits can be set at an index and retrieved from the same index.
|
||||
TEST_F(ITypBitsetTest, Indexing) {
|
||||
Bitset bits;
|
||||
ExpectBits(bits, {});
|
||||
|
||||
bits[Key(2)] = true;
|
||||
bits[Key(4)] = false;
|
||||
bits.set(Key(1));
|
||||
bits.set(Key(7), true);
|
||||
bits.set(Key(8), false);
|
||||
|
||||
ExpectBits(bits, {1, 2, 7});
|
||||
|
||||
bits.reset(Key(2));
|
||||
bits.reset(Key(7));
|
||||
ExpectBits(bits, {1});
|
||||
}
|
||||
|
||||
// Test that bits can be flipped
|
||||
TEST_F(ITypBitsetTest, Flip) {
|
||||
Bitset bits = {1 << 1 | 1 << 2 | 1 << 7};
|
||||
ExpectBits(bits, {1, 2, 7});
|
||||
|
||||
bits.flip(Key(4));
|
||||
bits.flip(Key(1)); // false
|
||||
bits.flip(Key(6));
|
||||
bits.flip(Key(5));
|
||||
ExpectBits(bits, {2, 4, 5, 6, 7});
|
||||
|
||||
bits.flip();
|
||||
ExpectBits(bits, {0, 1, 3, 8});
|
||||
|
||||
ExpectBits(~bits, {2, 4, 5, 6, 7});
|
||||
}
|
||||
|
||||
// Test that all the bits can be set/reset.
|
||||
TEST_F(ITypBitsetTest, SetResetAll) {
|
||||
Bitset bits;
|
||||
|
||||
bits.set();
|
||||
|
||||
ASSERT_EQ(bits.count(), 9u);
|
||||
ASSERT_TRUE(bits.all());
|
||||
ASSERT_TRUE(bits.any());
|
||||
ASSERT_FALSE(bits.none());
|
||||
|
||||
for (Key i(0); i < Key(9); ++i) {
|
||||
ASSERT_TRUE(bits[i]);
|
||||
}
|
||||
|
||||
bits.reset();
|
||||
|
||||
ASSERT_EQ(bits.count(), 0u);
|
||||
ASSERT_FALSE(bits.all());
|
||||
ASSERT_FALSE(bits.any());
|
||||
ASSERT_TRUE(bits.none());
|
||||
|
||||
for (Key i(0); i < Key(9); ++i) {
|
||||
ASSERT_FALSE(bits[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Test And operations
|
||||
TEST_F(ITypBitsetTest, And) {
|
||||
Bitset bits = {1 << 1 | 1 << 2 | 1 << 7};
|
||||
ExpectBits(bits, {1, 2, 7});
|
||||
|
||||
Bitset bits2 = bits & Bitset{1 << 0 | 1 << 3 | 1 << 7};
|
||||
ExpectBits(bits2, {7});
|
||||
ExpectBits(bits, {1, 2, 7});
|
||||
|
||||
bits &= Bitset{1 << 1 | 1 << 6};
|
||||
ExpectBits(bits, {1});
|
||||
}
|
||||
|
||||
// Test Or operations
|
||||
TEST_F(ITypBitsetTest, Or) {
|
||||
Bitset bits = {1 << 1 | 1 << 2 | 1 << 7};
|
||||
ExpectBits(bits, {1, 2, 7});
|
||||
|
||||
Bitset bits2 = bits | Bitset{1 << 0 | 1 << 3 | 1 << 7};
|
||||
ExpectBits(bits2, {0, 1, 2, 3, 7});
|
||||
ExpectBits(bits, {1, 2, 7});
|
||||
|
||||
bits |= Bitset{1 << 1 | 1 << 6};
|
||||
ExpectBits(bits, {1, 2, 6, 7});
|
||||
}
|
||||
|
||||
// Test xor operations
|
||||
TEST_F(ITypBitsetTest, Xor) {
|
||||
Bitset bits = {1 << 1 | 1 << 2 | 1 << 7};
|
||||
ExpectBits(bits, {1, 2, 7});
|
||||
|
||||
Bitset bits2 = bits ^ Bitset { 1 << 0 | 1 << 3 | 1 << 7 };
|
||||
ExpectBits(bits2, {0, 1, 2, 3});
|
||||
ExpectBits(bits, {1, 2, 7});
|
||||
|
||||
bits ^= Bitset{1 << 1 | 1 << 6};
|
||||
ExpectBits(bits, {2, 6, 7});
|
||||
}
|
||||
81
src/tests/unittests/ITypSpanTests.cpp
Normal file
81
src/tests/unittests/ITypSpanTests.cpp
Normal file
@@ -0,0 +1,81 @@
|
||||
// Copyright 2020 The Dawn 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 <gtest/gtest.h>
|
||||
|
||||
#include "common/TypedInteger.h"
|
||||
#include "common/ityp_span.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
class ITypSpanTest : public testing::Test {
|
||||
protected:
|
||||
using Key = TypedInteger<struct KeyT, size_t>;
|
||||
using Val = TypedInteger<struct ValT, uint32_t>;
|
||||
using Span = ityp::span<Key, Val>;
|
||||
};
|
||||
|
||||
// Test that values can be set at an index and retrieved from the same index.
|
||||
TEST_F(ITypSpanTest, Indexing) {
|
||||
std::array<Val, 10> arr;
|
||||
Span span(arr.data(), Key(arr.size()));
|
||||
{
|
||||
span[Key(2)] = Val(5);
|
||||
span[Key(1)] = Val(9);
|
||||
span[Key(9)] = Val(2);
|
||||
|
||||
ASSERT_EQ(span[Key(2)], Val(5));
|
||||
ASSERT_EQ(span[Key(1)], Val(9));
|
||||
ASSERT_EQ(span[Key(9)], Val(2));
|
||||
}
|
||||
}
|
||||
|
||||
// Test that the span can be is iterated in order with a range-based for loop
|
||||
TEST_F(ITypSpanTest, RangeBasedIteration) {
|
||||
std::array<Val, 10> arr;
|
||||
Span span(arr.data(), Key(arr.size()));
|
||||
|
||||
// Assign in a non-const range-based for loop
|
||||
uint32_t i = 0;
|
||||
for (Val& val : span) {
|
||||
val = Val(i);
|
||||
}
|
||||
|
||||
// Check values in a const range-based for loop
|
||||
i = 0;
|
||||
for (Val val : static_cast<const Span&>(span)) {
|
||||
ASSERT_EQ(val, span[Key(i++)]);
|
||||
}
|
||||
}
|
||||
|
||||
// Test that begin/end/front/back/data return pointers/references to the correct elements.
|
||||
TEST_F(ITypSpanTest, BeginEndFrontBackData) {
|
||||
std::array<Val, 10> arr;
|
||||
Span span(arr.data(), Key(arr.size()));
|
||||
|
||||
// non-const versions
|
||||
ASSERT_EQ(span.begin(), &span[Key(0)]);
|
||||
ASSERT_EQ(span.end(), &span[Key(0)] + static_cast<size_t>(span.size()));
|
||||
ASSERT_EQ(&span.front(), &span[Key(0)]);
|
||||
ASSERT_EQ(&span.back(), &span[Key(9)]);
|
||||
ASSERT_EQ(span.data(), &span[Key(0)]);
|
||||
|
||||
// const versions
|
||||
const Span& constSpan = span;
|
||||
ASSERT_EQ(constSpan.begin(), &constSpan[Key(0)]);
|
||||
ASSERT_EQ(constSpan.end(), &constSpan[Key(0)] + static_cast<size_t>(constSpan.size()));
|
||||
ASSERT_EQ(&constSpan.front(), &constSpan[Key(0)]);
|
||||
ASSERT_EQ(&constSpan.back(), &constSpan[Key(9)]);
|
||||
ASSERT_EQ(constSpan.data(), &constSpan[Key(0)]);
|
||||
}
|
||||
234
src/tests/unittests/TypedIntegerTests.cpp
Normal file
234
src/tests/unittests/TypedIntegerTests.cpp
Normal file
@@ -0,0 +1,234 @@
|
||||
// Copyright 2020 The Dawn 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 <gtest/gtest.h>
|
||||
|
||||
#include "common/TypedInteger.h"
|
||||
#include "common/UnderlyingType.h"
|
||||
|
||||
class TypedIntegerTest : public testing::Test {
|
||||
protected:
|
||||
using Unsigned = TypedInteger<struct UnsignedT, uint32_t>;
|
||||
using Signed = TypedInteger<struct SignedT, int32_t>;
|
||||
};
|
||||
|
||||
// Test that typed integers can be created and cast and the internal values are identical
|
||||
TEST_F(TypedIntegerTest, ConstructionAndCast) {
|
||||
Signed svalue(2);
|
||||
EXPECT_EQ(static_cast<int32_t>(svalue), 2);
|
||||
|
||||
Unsigned uvalue(7);
|
||||
EXPECT_EQ(static_cast<uint32_t>(uvalue), 7u);
|
||||
|
||||
static_assert(static_cast<int32_t>(Signed(3)) == 3, "");
|
||||
static_assert(static_cast<uint32_t>(Unsigned(28)) == 28, "");
|
||||
}
|
||||
|
||||
// Test typed integer comparison operators
|
||||
TEST_F(TypedIntegerTest, Comparison) {
|
||||
Unsigned value(8);
|
||||
|
||||
// Truthy usages of comparison operators
|
||||
EXPECT_TRUE(value < Unsigned(9));
|
||||
EXPECT_TRUE(value <= Unsigned(9));
|
||||
EXPECT_TRUE(value <= Unsigned(8));
|
||||
EXPECT_TRUE(value == Unsigned(8));
|
||||
EXPECT_TRUE(value >= Unsigned(8));
|
||||
EXPECT_TRUE(value >= Unsigned(7));
|
||||
EXPECT_TRUE(value > Unsigned(7));
|
||||
EXPECT_TRUE(value != Unsigned(7));
|
||||
|
||||
// Falsy usages of comparison operators
|
||||
EXPECT_FALSE(value >= Unsigned(9));
|
||||
EXPECT_FALSE(value > Unsigned(9));
|
||||
EXPECT_FALSE(value > Unsigned(8));
|
||||
EXPECT_FALSE(value != Unsigned(8));
|
||||
EXPECT_FALSE(value < Unsigned(8));
|
||||
EXPECT_FALSE(value < Unsigned(7));
|
||||
EXPECT_FALSE(value <= Unsigned(7));
|
||||
EXPECT_FALSE(value == Unsigned(7));
|
||||
}
|
||||
|
||||
TEST_F(TypedIntegerTest, Arithmetic) {
|
||||
// Postfix Increment
|
||||
{
|
||||
Signed value(0);
|
||||
EXPECT_EQ(value++, Signed(0));
|
||||
EXPECT_EQ(value, Signed(1));
|
||||
}
|
||||
|
||||
// Prefix Increment
|
||||
{
|
||||
Signed value(0);
|
||||
EXPECT_EQ(++value, Signed(1));
|
||||
EXPECT_EQ(value, Signed(1));
|
||||
}
|
||||
|
||||
// Postfix Decrement
|
||||
{
|
||||
Signed value(0);
|
||||
EXPECT_EQ(value--, Signed(0));
|
||||
EXPECT_EQ(value, Signed(-1));
|
||||
}
|
||||
|
||||
// Prefix Decrement
|
||||
{
|
||||
Signed value(0);
|
||||
EXPECT_EQ(--value, Signed(-1));
|
||||
EXPECT_EQ(value, Signed(-1));
|
||||
}
|
||||
|
||||
// Signed addition
|
||||
{
|
||||
Signed a(3);
|
||||
Signed b(-4);
|
||||
Signed c = a + b;
|
||||
EXPECT_EQ(a, Signed(3));
|
||||
EXPECT_EQ(b, Signed(-4));
|
||||
EXPECT_EQ(c, Signed(-1));
|
||||
}
|
||||
|
||||
// Signed subtraction
|
||||
{
|
||||
Signed a(3);
|
||||
Signed b(-4);
|
||||
Signed c = a - b;
|
||||
EXPECT_EQ(a, Signed(3));
|
||||
EXPECT_EQ(b, Signed(-4));
|
||||
EXPECT_EQ(c, Signed(7));
|
||||
}
|
||||
|
||||
// Unsigned addition
|
||||
{
|
||||
Unsigned a(9);
|
||||
Unsigned b(3);
|
||||
Unsigned c = a + b;
|
||||
EXPECT_EQ(a, Unsigned(9));
|
||||
EXPECT_EQ(b, Unsigned(3));
|
||||
EXPECT_EQ(c, Unsigned(12));
|
||||
}
|
||||
|
||||
// Unsigned subtraction
|
||||
{
|
||||
Unsigned a(9);
|
||||
Unsigned b(2);
|
||||
Unsigned c = a - b;
|
||||
EXPECT_EQ(a, Unsigned(9));
|
||||
EXPECT_EQ(b, Unsigned(2));
|
||||
EXPECT_EQ(c, Unsigned(7));
|
||||
}
|
||||
|
||||
// Negation
|
||||
{
|
||||
Signed a(5);
|
||||
Signed b = -a;
|
||||
EXPECT_EQ(a, Signed(5));
|
||||
EXPECT_EQ(b, Signed(-5));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TypedIntegerTest, NumericLimits) {
|
||||
EXPECT_EQ(std::numeric_limits<Unsigned>::max(), Unsigned(std::numeric_limits<uint32_t>::max()));
|
||||
EXPECT_EQ(std::numeric_limits<Unsigned>::min(), Unsigned(std::numeric_limits<uint32_t>::min()));
|
||||
EXPECT_EQ(std::numeric_limits<Signed>::max(), Signed(std::numeric_limits<int32_t>::max()));
|
||||
EXPECT_EQ(std::numeric_limits<Signed>::min(), Signed(std::numeric_limits<int32_t>::min()));
|
||||
}
|
||||
|
||||
TEST_F(TypedIntegerTest, UnderlyingType) {
|
||||
static_assert(std::is_same<UnderlyingType<Unsigned>, uint32_t>::value, "");
|
||||
static_assert(std::is_same<UnderlyingType<Signed>, int32_t>::value, "");
|
||||
}
|
||||
|
||||
// Tests for bounds assertions on arithmetic overflow and underflow.
|
||||
#if defined(DAWN_ENABLE_ASSERTS)
|
||||
|
||||
TEST_F(TypedIntegerTest, IncrementUnsignedOverflow) {
|
||||
Unsigned value(std::numeric_limits<uint32_t>::max() - 1);
|
||||
|
||||
value++; // Doesn't overflow.
|
||||
EXPECT_DEATH(value++, ""); // Overflows.
|
||||
}
|
||||
|
||||
TEST_F(TypedIntegerTest, IncrementSignedOverflow) {
|
||||
Signed value(std::numeric_limits<int32_t>::max() - 1);
|
||||
|
||||
value++; // Doesn't overflow.
|
||||
EXPECT_DEATH(value++, ""); // Overflows.
|
||||
}
|
||||
|
||||
TEST_F(TypedIntegerTest, DecrementUnsignedUnderflow) {
|
||||
Unsigned value(std::numeric_limits<uint32_t>::min() + 1);
|
||||
|
||||
value--; // Doesn't underflow.
|
||||
EXPECT_DEATH(value--, ""); // Underflows.
|
||||
}
|
||||
|
||||
TEST_F(TypedIntegerTest, DecrementSignedUnderflow) {
|
||||
Signed value(std::numeric_limits<int32_t>::min() + 1);
|
||||
|
||||
value--; // Doesn't underflow.
|
||||
EXPECT_DEATH(value--, ""); // Underflows.
|
||||
}
|
||||
|
||||
TEST_F(TypedIntegerTest, UnsignedAdditionOverflow) {
|
||||
Unsigned value(std::numeric_limits<uint32_t>::max() - 1);
|
||||
|
||||
value + Unsigned(1); // Doesn't overflow.
|
||||
EXPECT_DEATH(value + Unsigned(2), ""); // Overflows.
|
||||
}
|
||||
|
||||
TEST_F(TypedIntegerTest, UnsignedSubtractionUnderflow) {
|
||||
Unsigned value(1);
|
||||
|
||||
value - Unsigned(1); // Doesn't underflow.
|
||||
EXPECT_DEATH(value - Unsigned(2), ""); // Underflows.
|
||||
}
|
||||
|
||||
TEST_F(TypedIntegerTest, SignedAdditionOverflow) {
|
||||
Signed value(std::numeric_limits<int32_t>::max() - 1);
|
||||
|
||||
value + Signed(1); // Doesn't overflow.
|
||||
EXPECT_DEATH(value + Signed(2), ""); // Overflows.
|
||||
}
|
||||
|
||||
TEST_F(TypedIntegerTest, SignedAdditionUnderflow) {
|
||||
Signed value(std::numeric_limits<int32_t>::min() + 1);
|
||||
|
||||
value + Signed(-1); // Doesn't underflow.
|
||||
EXPECT_DEATH(value + Signed(-2), ""); // Underflows.
|
||||
}
|
||||
|
||||
TEST_F(TypedIntegerTest, SignedSubtractionOverflow) {
|
||||
Signed value(std::numeric_limits<int32_t>::max() - 1);
|
||||
|
||||
value - Signed(-1); // Doesn't overflow.
|
||||
EXPECT_DEATH(value - Signed(-2), ""); // Overflows.
|
||||
}
|
||||
|
||||
TEST_F(TypedIntegerTest, SignedSubtractionUnderflow) {
|
||||
Signed value(std::numeric_limits<int32_t>::min() + 1);
|
||||
|
||||
value - Signed(1); // Doesn't underflow.
|
||||
EXPECT_DEATH(value - Signed(2), ""); // Underflows.
|
||||
}
|
||||
|
||||
TEST_F(TypedIntegerTest, NegationOverflow) {
|
||||
Signed maxValue(std::numeric_limits<int32_t>::max());
|
||||
-maxValue; // Doesn't underflow.
|
||||
|
||||
Signed minValue(std::numeric_limits<int32_t>::min());
|
||||
EXPECT_DEATH(-minValue, ""); // Overflows.
|
||||
}
|
||||
|
||||
#endif // defined(DAWN_ENABLE_ASSERTS)
|
||||
Reference in New Issue
Block a user