blob: c5e98b31909985e2c219817ee0dffba8ca4992e6 [file] [edit]
/*
* 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]}");
}