blob: f05fd80e2ffb79c44658537f69d2115888f517eb [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/iu64.h"
#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]");
}
// ============================================================================
// Span<IU64> tests (corner cases, sign mixing, large range)
// ============================================================================
TEST(SpanIU64Test, FullAndLimits) {
EXPECT_EQ(Span<IU64>::Min, IU64(std::numeric_limits<int64_t>::min()));
EXPECT_EQ(Span<IU64>::Max, IU64(std::numeric_limits<uint64_t>::max()));
Span<IU64> full = Span<IU64>::full();
EXPECT_TRUE(full.isFull());
EXPECT_FALSE(full.isEmpty());
EXPECT_EQ(full.min, IU64(std::numeric_limits<int64_t>::min()));
EXPECT_EQ(full.max, IU64(std::numeric_limits<uint64_t>::max()));
// Default constructed span is full
Span<IU64> def;
EXPECT_TRUE(def.isFull());
EXPECT_FALSE(def.isEmpty());
EXPECT_EQ(def, full);
}
TEST(SpanIU64Test, Empty) {
Span<IU64> empty = Span<IU64>::empty();
EXPECT_TRUE(empty.isEmpty());
EXPECT_FALSE(empty.isFull());
Span<IU64> empty2{IU64(100), IU64(-100)};
EXPECT_TRUE(empty2.isEmpty());
EXPECT_FALSE(empty2.isFull());
EXPECT_EQ(empty, empty2);
Span<IU64> empty3{IU64(uint64_t(1)), IU64(int64_t(-1))};
EXPECT_TRUE(empty3.isEmpty());
EXPECT_EQ(empty, empty3);
}
TEST(SpanIU64Test, SingletonsAtExtremes) {
// Min int64 singleton
Span<IU64> minI64{IU64(std::numeric_limits<int64_t>::min()),
IU64(std::numeric_limits<int64_t>::min())};
EXPECT_FALSE(minI64.isEmpty());
EXPECT_FALSE(minI64.isFull());
EXPECT_EQ(minI64.min, IU64(std::numeric_limits<int64_t>::min()));
EXPECT_EQ(minI64.max, IU64(std::numeric_limits<int64_t>::min()));
// -1 singleton
Span<IU64> negOne{IU64(-1), IU64(-1)};
EXPECT_FALSE(negOne.isEmpty());
// 0 singleton
Span<IU64> zero{IU64(0), IU64(0)};
EXPECT_FALSE(zero.isEmpty());
// 1 singleton
Span<IU64> one{IU64(1), IU64(1)};
EXPECT_FALSE(one.isEmpty());
// Max int64 singleton
Span<IU64> maxI64{IU64(std::numeric_limits<int64_t>::max()),
IU64(std::numeric_limits<int64_t>::max())};
EXPECT_FALSE(maxI64.isEmpty());
// 2^63 singleton (above int64_t max, into uint64_t territory)
Span<IU64> highBit{IU64(uint64_t(1) << 63), IU64(uint64_t(1) << 63)};
EXPECT_FALSE(highBit.isEmpty());
// Max uint64 singleton
Span<IU64> maxU64{IU64(std::numeric_limits<uint64_t>::max()),
IU64(std::numeric_limits<uint64_t>::max())};
EXPECT_FALSE(maxU64.isEmpty());
}
TEST(SpanIU64Test, CrossingZero) {
Span<IU64> span{IU64(-10), IU64(10)};
EXPECT_FALSE(span.isEmpty());
EXPECT_FALSE(span.isFull());
// Contains points inside
EXPECT_TRUE(span.contains(Span<IU64>(IU64(-10), IU64(-10))));
EXPECT_TRUE(span.contains(Span<IU64>(IU64(-5), IU64(5))));
EXPECT_TRUE(span.contains(Span<IU64>(IU64(0), IU64(0))));
EXPECT_TRUE(span.contains(Span<IU64>(IU64(10), IU64(10))));
// Does not contain points outside
EXPECT_FALSE(span.contains(Span<IU64>(IU64(-11), IU64(-11))));
EXPECT_FALSE(span.contains(Span<IU64>(IU64(11), IU64(11))));
EXPECT_FALSE(span.contains(Span<IU64>(IU64(-15), IU64(5))));
EXPECT_FALSE(span.contains(Span<IU64>(IU64(-5), IU64(15))));
}
TEST(SpanIU64Test, NegativeAndPositiveIntersections) {
Span<IU64> neg{IU64(-100), IU64(-10)};
Span<IU64> pos{IU64(10), IU64(100)};
EXPECT_FALSE(neg.hasOverlap(pos));
EXPECT_FALSE(pos.hasOverlap(neg));
EXPECT_TRUE(neg.intersection(pos).isEmpty());
EXPECT_TRUE(pos.intersection(neg).isEmpty());
Span<IU64> touchNegZero{IU64(-10), IU64(0)};
Span<IU64> touchZeroPos{IU64(0), IU64(10)};
EXPECT_TRUE(touchNegZero.hasOverlap(touchZeroPos));
EXPECT_EQ(touchNegZero.intersection(touchZeroPos),
Span<IU64>(IU64(0), IU64(0)));
Span<IU64> overlap{IU64(-50), IU64(50)};
EXPECT_EQ(neg.intersection(overlap), Span<IU64>(IU64(-50), IU64(-10)));
EXPECT_EQ(pos.intersection(overlap), Span<IU64>(IU64(10), IU64(50)));
}
TEST(SpanIU64Test, SignedUnsignedBoundary) {
// Test around INT64_MAX and 2^63
int64_t maxI64 = std::numeric_limits<int64_t>::max();
uint64_t highBit = uint64_t(maxI64) + 1; // 0x8000000000000000ULL
Span<IU64> s1(IU64(maxI64 - 100), IU64(highBit + 50));
Span<IU64> s2(IU64(highBit), IU64(highBit + 100));
EXPECT_TRUE(s1.hasOverlap(s2));
EXPECT_EQ(s1.intersection(s2), Span<IU64>(IU64(highBit), IU64(highBit + 50)));
// Disjoint near 2^63 boundary
Span<IU64> s3{IU64(maxI64 - 200), IU64(maxI64)};
Span<IU64> s4{IU64(highBit + 1), IU64(highBit + 100)};
EXPECT_FALSE(s3.hasOverlap(s4));
EXPECT_TRUE(s3.intersection(s4).isEmpty());
// Adjacent touching at 2^63
Span<IU64> s5{IU64(maxI64), IU64(highBit)};
Span<IU64> s6{IU64(highBit), IU64(highBit + 10)};
EXPECT_TRUE(s5.hasOverlap(s6));
EXPECT_EQ(s5.intersection(s6), Span<IU64>(IU64(highBit), IU64(highBit)));
}
TEST(SpanIU64Test, ExtremeBoundaries) {
Span<IU64> minPart(IU64(std::numeric_limits<int64_t>::min()),
IU64(std::numeric_limits<int64_t>::min() + 100));
Span<IU64> maxPart(IU64(std::numeric_limits<uint64_t>::max() - 100),
IU64(std::numeric_limits<uint64_t>::max()));
EXPECT_FALSE(minPart.hasOverlap(maxPart));
EXPECT_TRUE(minPart.intersection(maxPart).isEmpty());
Span<IU64> full = Span<IU64>::full();
EXPECT_TRUE(full.contains(minPart));
EXPECT_TRUE(full.contains(maxPart));
EXPECT_EQ(full.intersection(minPart), minPart);
EXPECT_EQ(full.intersection(maxPart), maxPart);
EXPECT_TRUE(full.hasOverlap(minPart));
EXPECT_TRUE(full.hasOverlap(maxPart));
Span<IU64> allNeg(IU64(std::numeric_limits<int64_t>::min()), IU64(-1));
Span<IU64> allNonNeg(IU64(0), IU64(std::numeric_limits<uint64_t>::max()));
EXPECT_FALSE(allNeg.hasOverlap(allNonNeg));
EXPECT_TRUE(allNeg.intersection(allNonNeg).isEmpty());
EXPECT_TRUE(full.contains(allNeg));
EXPECT_TRUE(full.contains(allNonNeg));
}
TEST(SpanIU64Test, SetAndMutate) {
Span<IU64> s;
EXPECT_TRUE(s.isFull());
s.set(IU64(-12345));
EXPECT_FALSE(s.isFull());
EXPECT_FALSE(s.isEmpty());
EXPECT_EQ(s.min, IU64(-12345));
EXPECT_EQ(s.max, IU64(-12345));
s.setEmpty();
EXPECT_TRUE(s.isEmpty());
s.setFull();
EXPECT_TRUE(s.isFull());
}