| /* | |
| * Copyright 2026 WebAssembly Community Group participants | |
| * | |
| * 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 <cstdint> | |
| #include <limits> | |
| #include <sstream> | |
| #include "support/span.h" | |
| #include "gtest/gtest.h" | |
| using namespace wasm; | |
| // ============================================================================ | |
| // Generic Span<T> tests | |
| // ============================================================================ | |
| TEST(SpanTest, EmptySpanInt) { | |
| Span<int32_t> empty = Span<int32_t>::empty(); | |
| EXPECT_TRUE(empty.isEmpty()); | |
| EXPECT_FALSE(empty.isFull()); | |
| Span<int32_t> invalid(10, 5); | |
| EXPECT_TRUE(invalid.isEmpty()); | |
| EXPECT_FALSE(invalid.isFull()); | |
| EXPECT_EQ(empty, invalid); | |
| Span<int32_t> s; | |
| EXPECT_FALSE(s.isEmpty()); | |
| s.setEmpty(); | |
| EXPECT_TRUE(s.isEmpty()); | |
| EXPECT_EQ(s, empty); | |
| } | |
| TEST(SpanTest, FullSpanIntTypes) { | |
| // Signed 32-bit | |
| Span<int32_t> fullI32 = Span<int32_t>::full(); | |
| EXPECT_TRUE(fullI32.isFull()); | |
| EXPECT_FALSE(fullI32.isEmpty()); | |
| EXPECT_EQ(fullI32.min, std::numeric_limits<int32_t>::min()); | |
| EXPECT_EQ(fullI32.max, std::numeric_limits<int32_t>::max()); | |
| Span<int32_t> defI32; | |
| EXPECT_TRUE(defI32.isFull()); | |
| EXPECT_EQ(defI32, fullI32); | |
| // Unsigned 32-bit | |
| Span<uint32_t> fullU32 = Span<uint32_t>::full(); | |
| EXPECT_TRUE(fullU32.isFull()); | |
| EXPECT_FALSE(fullU32.isEmpty()); | |
| EXPECT_EQ(fullU32.min, 0u); | |
| EXPECT_EQ(fullU32.max, std::numeric_limits<uint32_t>::max()); | |
| // Signed 64-bit | |
| Span<int64_t> fullI64 = Span<int64_t>::full(); | |
| EXPECT_TRUE(fullI64.isFull()); | |
| EXPECT_FALSE(fullI64.isEmpty()); | |
| EXPECT_EQ(fullI64.min, std::numeric_limits<int64_t>::min()); | |
| EXPECT_EQ(fullI64.max, std::numeric_limits<int64_t>::max()); | |
| // Unsigned 64-bit | |
| Span<uint64_t> fullU64 = Span<uint64_t>::full(); | |
| EXPECT_TRUE(fullU64.isFull()); | |
| EXPECT_FALSE(fullU64.isEmpty()); | |
| EXPECT_EQ(fullU64.min, 0ull); | |
| EXPECT_EQ(fullU64.max, std::numeric_limits<uint64_t>::max()); | |
| } | |
| TEST(SpanTest, SetSingleValue) { | |
| Span<int32_t> s; | |
| s.set(42); | |
| EXPECT_EQ(s.min, 42); | |
| EXPECT_EQ(s.max, 42); | |
| EXPECT_FALSE(s.isEmpty()); | |
| EXPECT_FALSE(s.isFull()); | |
| EXPECT_EQ(s, Span<int32_t>(42, 42)); | |
| } | |
| TEST(SpanTest, SetFull) { | |
| Span<int32_t> s(10, 20); | |
| EXPECT_FALSE(s.isFull()); | |
| s.setFull(); | |
| EXPECT_TRUE(s.isFull()); | |
| EXPECT_EQ(s.min, std::numeric_limits<int32_t>::min()); | |
| EXPECT_EQ(s.max, std::numeric_limits<int32_t>::max()); | |
| } | |
| TEST(SpanTest, IntersectionInt) { | |
| Span<int32_t> a(1, 10); | |
| Span<int32_t> b(5, 15); | |
| Span<int32_t> ab = a.intersection(b); | |
| EXPECT_EQ(ab, Span<int32_t>(5, 10)); | |
| // Commutativity | |
| EXPECT_EQ(b.intersection(a), Span<int32_t>(5, 10)); | |
| // Touching at a single point | |
| Span<int32_t> c(10, 20); | |
| EXPECT_EQ(a.intersection(c), Span<int32_t>(10, 10)); | |
| // Disjoint | |
| Span<int32_t> d(11, 20); | |
| EXPECT_TRUE(a.intersection(d).isEmpty()); | |
| EXPECT_EQ(a.intersection(d), Span<int32_t>::empty()); | |
| // Contained | |
| Span<int32_t> e(3, 7); | |
| EXPECT_EQ(a.intersection(e), Span<int32_t>(3, 7)); | |
| // Identical | |
| EXPECT_EQ(a.intersection(a), a); | |
| // With empty | |
| EXPECT_TRUE(a.intersection(Span<int32_t>::empty()).isEmpty()); | |
| EXPECT_TRUE(Span<int32_t>::empty().intersection(a).isEmpty()); | |
| // With full | |
| EXPECT_EQ(a.intersection(Span<int32_t>::full()), a); | |
| EXPECT_EQ(Span<int32_t>::full().intersection(a), a); | |
| } | |
| TEST(SpanTest, HasOverlapInt) { | |
| Span<int32_t> a(1, 10); | |
| Span<int32_t> b(5, 15); | |
| Span<int32_t> c(10, 20); | |
| Span<int32_t> d(11, 20); | |
| EXPECT_TRUE(a.hasOverlap(b)); | |
| EXPECT_TRUE(b.hasOverlap(a)); | |
| EXPECT_TRUE(a.hasOverlap(c)); | |
| EXPECT_FALSE(a.hasOverlap(d)); | |
| EXPECT_FALSE(d.hasOverlap(a)); | |
| EXPECT_FALSE(a.hasOverlap(Span<int32_t>::empty())); | |
| EXPECT_TRUE(a.hasOverlap(Span<int32_t>::full())); | |
| EXPECT_FALSE(Span<int32_t>::empty().hasOverlap(Span<int32_t>::full())); | |
| } | |
| TEST(SpanTest, ContainsInt) { | |
| Span<int32_t> a(1, 10); | |
| Span<int32_t> b(3, 7); | |
| Span<int32_t> c(5, 15); | |
| Span<int32_t> d(11, 20); | |
| EXPECT_TRUE(a.contains(b)); | |
| EXPECT_FALSE(b.contains(a)); | |
| EXPECT_TRUE(a.contains(a)); | |
| EXPECT_FALSE(a.contains(c)); | |
| EXPECT_FALSE(a.contains(d)); | |
| EXPECT_TRUE(a.contains(Span<int32_t>::empty())); | |
| EXPECT_TRUE(Span<int32_t>::empty().contains(Span<int32_t>::empty())); | |
| EXPECT_FALSE(Span<int32_t>::empty().contains(a)); | |
| EXPECT_TRUE(Span<int32_t>::full().contains(a)); | |
| EXPECT_TRUE(Span<int32_t>::full().contains(Span<int32_t>::empty())); | |
| EXPECT_FALSE(a.contains(Span<int32_t>::full())); | |
| } | |
| TEST(SpanTest, StreamOutput) { | |
| auto toString = [](const auto& span) { | |
| std::ostringstream ss; | |
| ss << span; | |
| return ss.str(); | |
| }; | |
| EXPECT_EQ(toString(Span<int32_t>(1, 10)), "[1, 10]"); | |
| EXPECT_EQ(toString(Span<int32_t>::empty()), "[empty]"); | |
| EXPECT_EQ(toString(Span<int32_t>(10, 5)), "[empty]"); | |
| } | |
| // ============================================================================ | |
| // Spans<T, N> and SpansU2 tests | |
| // ============================================================================ | |
| TEST(SpansTest, Construction) { | |
| SpansU2 empty; | |
| EXPECT_TRUE(empty.empty()); | |
| EXPECT_EQ(empty.size(), 0u); | |
| SpansU2 fromSpans{Span<uint64_t>(0, 10), Span<uint64_t>(20, 30)}; | |
| EXPECT_FALSE(fromSpans.empty()); | |
| EXPECT_EQ(fromSpans.size(), 2u); | |
| EXPECT_EQ(fromSpans[0], Span<uint64_t>(0, 10)); | |
| EXPECT_EQ(fromSpans[1], Span<uint64_t>(20, 30)); | |
| SpansU2 fromCoords{{0, 10}, {20, 30}}; | |
| EXPECT_EQ(fromCoords.size(), 2u); | |
| EXPECT_EQ(fromCoords[0], Span<uint64_t>(0, 10)); | |
| EXPECT_EQ(fromCoords[1], Span<uint64_t>(20, 30)); | |
| SpansU2 single{{5, 15}}; | |
| EXPECT_EQ(single.size(), 1u); | |
| EXPECT_EQ(single[0], Span<uint64_t>(5, 15)); | |
| } | |
| TEST(SpansTest, Equality) { | |
| EXPECT_EQ(SpansU2(), SpansU2()); | |
| EXPECT_EQ((SpansU2{{0, 10}}), (SpansU2{{0, 10}})); | |
| EXPECT_EQ((SpansU2{{0, 10}, {20, 30}}), (SpansU2{{0, 10}, {20, 30}})); | |
| EXPECT_NE((SpansU2{{0, 10}}), SpansU2()); | |
| EXPECT_NE((SpansU2{{0, 10}}), (SpansU2{{0, 11}})); | |
| EXPECT_NE((SpansU2{{0, 10}}), (SpansU2{{0, 10}, {20, 30}})); | |
| } | |
| TEST(SpansTest, HasOverlap) { | |
| SpansU2 empty; | |
| SpansU2 s1{{0, 10}, {20, 30}}; | |
| SpansU2 s2{{5, 15}}; | |
| SpansU2 s3{{25, 35}}; | |
| SpansU2 s4{{11, 19}}; | |
| SpansU2 s5{{31, 40}}; | |
| SpansU2 s6{{10, 20}}; | |
| EXPECT_FALSE(empty.hasOverlap(s1)); | |
| EXPECT_FALSE(s1.hasOverlap(empty)); | |
| EXPECT_FALSE(empty.hasOverlap(empty)); | |
| // Overlap with first span | |
| EXPECT_TRUE(s1.hasOverlap(s2)); | |
| EXPECT_TRUE(s2.hasOverlap(s1)); | |
| // Overlap with second span | |
| EXPECT_TRUE(s1.hasOverlap(s3)); | |
| EXPECT_TRUE(s3.hasOverlap(s1)); | |
| // In the gap between spans: no overlap | |
| EXPECT_FALSE(s1.hasOverlap(s4)); | |
| EXPECT_FALSE(s4.hasOverlap(s1)); | |
| // Beyond all spans: no overlap | |
| EXPECT_FALSE(s1.hasOverlap(s5)); | |
| EXPECT_FALSE(s5.hasOverlap(s1)); | |
| // Touching at endpoints: overlaps | |
| EXPECT_TRUE(s1.hasOverlap(s6)); | |
| EXPECT_TRUE(s6.hasOverlap(s1)); | |
| } | |
| TEST(SpansTest, Contains) { | |
| SpansU2 empty; | |
| SpansU2 s1{{0, 100}, {200, 300}}; | |
| SpansU2 s2{{10, 20}}; | |
| SpansU2 s3{{210, 220}}; | |
| SpansU2 s4{{10, 20}, {210, 220}}; | |
| SpansU2 s5{{50, 150}}; | |
| SpansU2 s6{{10, 20}, {250, 350}}; | |
| // Empty contains empty, non-empty contains empty, empty does not contain | |
| // non-empty | |
| EXPECT_TRUE(empty.contains(empty)); | |
| EXPECT_TRUE(s1.contains(empty)); | |
| EXPECT_FALSE(empty.contains(s1)); | |
| // Identity | |
| EXPECT_TRUE(s1.contains(s1)); | |
| EXPECT_TRUE(s2.contains(s2)); | |
| // Contained within first span | |
| EXPECT_TRUE(s1.contains(s2)); | |
| EXPECT_FALSE(s2.contains(s1)); | |
| // Contained within second span | |
| EXPECT_TRUE(s1.contains(s3)); | |
| EXPECT_FALSE(s3.contains(s1)); | |
| // Multiple spans each contained in one of s1's spans | |
| EXPECT_TRUE(s1.contains(s4)); | |
| EXPECT_FALSE(s4.contains(s1)); | |
| // Straddles gap: not contained | |
| EXPECT_FALSE(s1.contains(s5)); | |
| // One span contained, but second span extends past s1: not contained | |
| EXPECT_FALSE(s1.contains(s6)); | |
| } | |
| TEST(SpansTest, ExtremeBoundaries) { | |
| uint64_t maxU64 = std::numeric_limits<uint64_t>::max(); | |
| uint64_t highBit = uint64_t(1) << 63; | |
| SpansU2 lowPart{{0, 100}}; | |
| SpansU2 highPart{{maxU64 - 100, maxU64}}; | |
| SpansU2 midPart{{highBit - 10, highBit + 10}}; | |
| EXPECT_FALSE(lowPart.hasOverlap(highPart)); | |
| EXPECT_FALSE(highPart.hasOverlap(lowPart)); | |
| EXPECT_FALSE(lowPart.hasOverlap(midPart)); | |
| EXPECT_FALSE(midPart.hasOverlap(highPart)); | |
| SpansU2 split{{0, 100}, {maxU64 - 100, maxU64}}; | |
| EXPECT_TRUE(split.contains(lowPart)); | |
| EXPECT_TRUE(split.contains(highPart)); | |
| EXPECT_FALSE(split.contains(midPart)); | |
| SpansU2 fullRange{{0, maxU64}}; | |
| EXPECT_TRUE(fullRange.contains(split)); | |
| EXPECT_TRUE(fullRange.contains(lowPart)); | |
| EXPECT_TRUE(fullRange.contains(highPart)); | |
| EXPECT_TRUE(fullRange.contains(midPart)); | |
| } | |
| TEST(SpansTest, Mutation) { | |
| SpansU2 s; | |
| EXPECT_TRUE(s.empty()); | |
| s.push_back(Span<uint64_t>(10, 20)); | |
| EXPECT_EQ(s.size(), 1u); | |
| EXPECT_EQ(s[0], Span<uint64_t>(10, 20)); | |
| s.push_back(Span<uint64_t>(30, 40)); | |
| EXPECT_EQ(s.size(), 2u); | |
| EXPECT_EQ(s[1], Span<uint64_t>(30, 40)); | |
| s.pop_back(); | |
| EXPECT_EQ(s.size(), 1u); | |
| EXPECT_EQ(s[0], Span<uint64_t>(10, 20)); | |
| s.clear(); | |
| EXPECT_TRUE(s.empty()); | |
| } | |
| TEST(SpansTest, StreamOutput) { | |
| auto toString = [](const auto& spans) { | |
| std::ostringstream ss; | |
| ss << spans; | |
| return ss.str(); | |
| }; | |
| EXPECT_EQ(toString(SpansU2{}), "{empty}"); | |
| EXPECT_EQ(toString(SpansU2{{1, 10}}), "{[1, 10]}"); | |
| EXPECT_EQ(toString(SpansU2{{1, 10}, {20, 30}}), "{[1, 10], [20, 30]}"); | |
| } |