wip(step097): checkpoint B fixes pending native release pairing
Some checks failed
validate-scripts / validate-scripts (push) Has been cancelled

This commit is contained in:
tei
2026-09-26 16:57:12 +09:00
parent b1239ac3b1
commit e463ba04ce
21 changed files with 789 additions and 41 deletions

View File

@@ -10,6 +10,7 @@
#include <algorithm>
#include <cstring>
#include <limits>
#include <vector>
using namespace mbgl;
@@ -63,7 +64,7 @@ struct Plane {
std::vector<uint8_t> bytes;
};
std::vector<uint8_t> qbin(uint64_t validTime, bool scalar = false) {
std::vector<uint8_t> qbin(uint64_t validTime, bool scalar = false, uint8_t validity = 0x0f) {
std::vector<Plane> planes;
if (scalar)
planes.push_back({99, 77, 0, 3, 0, 0, {1, 2, 3, 4}});
@@ -71,7 +72,15 @@ std::vector<uint8_t> qbin(uint64_t validTime, bool scalar = false) {
planes.push_back({1, 1, 1, 1, 1, 1, {1, 0, 2, 0, 3, 0, 4, 0}});
planes.push_back({2, 1, 1, 1, 1, 2, {5, 0, 6, 0, 7, 0, 8, 0}});
}
planes.push_back({3, 0, 0, 6, 3, 0, {15}});
for (auto& plane : planes) {
if (plane.type == 6) continue;
for (size_t cell = 0; cell < 4; ++cell) {
if (validity & (1u << cell)) continue;
const size_t bytesPerCell = plane.type == 1 || plane.type == 2 ? 2 : plane.type == 5 ? 4 : 1;
std::fill_n(plane.bytes.begin() + cell * bytesPerCell, bytesPerCell, uint8_t{0});
}
}
planes.push_back({3, 0, 0, 6, 3, 0, {validity}});
std::sort(planes.begin(), planes.end(), [](const Plane& a, const Plane& b) { return a.semantic < b.semantic; });
size_t payloadLength = 0;
for (const auto& plane : planes) payloadLength = aligned16(payloadLength + plane.bytes.size());
@@ -117,6 +126,11 @@ std::vector<uint8_t> qbin(uint64_t validTime, bool scalar = false) {
return bytes;
}
void recomputeQbinCrc(std::vector<uint8_t>& bytes) {
u32(bytes, 76, 0);
u32(bytes, 76, crc(bytes));
}
particle::ingress::EncodedFieldRuntimeMetadata metadata(const char* id, uint64_t generation, uint64_t version = 1) {
return {id, generation, version};
}
@@ -296,6 +310,62 @@ TEST(Source, RasterArrayV4HostApiRejectsKeepPublicProjectionStable) {
expectNoPartialMutation();
}
TEST(Source, RasterArrayV4RejectsNumericOverflowBeforeReplacingAcceptedRevision) {
style::RasterArraySource source{"v4-numeric-transaction"};
submit(source, "wind", "wind", Base, 1);
submit(source, "wind", "wind", Base + Hour, 2);
source.setParticleBinding({particle::ParticleBindingMode::VectorComponents,
"wind",
{1, 1, particle::FieldComponent::UEast},
{2, 1, particle::FieldComponent::VNorth},
{},
1});
ASSERT_TRUE(source.setRequestedForecastTime(Base + Hour / 2));
const auto baseline = source.resolveParticleProjection();
ASSERT_TRUE(baseline.accepted());
const auto materialized = source.impl().v4RendererState.particle;
ASSERT_TRUE(materialized);
const auto countersBefore = particle::rendererDebugCounterSnapshot();
auto overflowing = qbin(Base + Hour);
const size_t firstPlaneDescriptor = Header;
f32(overflowing, firstPlaneDescriptor + 4, std::numeric_limits<float>::max());
f32(overflowing, firstPlaneDescriptor + 8, std::numeric_limits<float>::max());
recomputeQbinCrc(overflowing);
EXPECT_EQ(source.submitEncodedField("wind", metadata("wind", 3, 2), windContract(), overflowing),
style::RasterArrayEncodedFieldSubmitResult::RejectedContract);
const auto after = source.resolveParticleProjection();
ASSERT_TRUE(after.accepted());
expectSameParticleProjection(*baseline.projection, *after.projection);
EXPECT_EQ(after.projection->t1Frame, baseline.projection->t1Frame);
EXPECT_EQ(source.impl().v4RendererState.particle, materialized);
const auto countersAfter = particle::rendererDebugCounterSnapshot();
EXPECT_EQ(countersAfter.v4ProjectionMaterialize, countersBefore.v4ProjectionMaterialize);
EXPECT_EQ(countersAfter.fieldTextureUpload, countersBefore.fieldTextureUpload);
}
TEST(Source, RasterArrayV4KeepsValidityZeroCellsAsLegalNoData) {
style::RasterArraySource source{"v4-validity-nodata"};
EXPECT_EQ(source.submitEncodedField("wind", metadata("wind", 1), windContract(), qbin(Base, false, 0x0e)),
style::RasterArrayEncodedFieldSubmitResult::AcceptedInserted);
submit(source, "wind", "wind", Base + Hour, 2);
source.setParticleBinding({particle::ParticleBindingMode::VectorComponents,
"wind",
{1, 1, particle::FieldComponent::UEast},
{2, 1, particle::FieldComponent::VNorth},
{},
1});
ASSERT_TRUE(source.setRequestedForecastTime(Base + Hour / 2));
const auto projection = source.resolveParticleProjection();
ASSERT_TRUE(projection.accepted());
const auto snapshots = particle::materializeParticleProjection(*projection.projection);
ASSERT_TRUE(snapshots);
ASSERT_EQ(snapshots->t0->valid.size(), 4u);
EXPECT_EQ(snapshots->t0->valid[0], 0);
EXPECT_EQ(snapshots->t0->valid[1], 1);
}
TEST(Source, RasterArrayV4HostApiResolvesIndependentBindingsFromOneForecastClock) {
style::RasterArraySource source{"v4"};
submit(source, "wind", "wind", Base, 1);