#include #include #include #include #include using namespace mbgl; namespace { void u16(std::vector& b,size_t o,uint16_t v){b[o]=v;b[o+1]=v>>8;} void u32(std::vector& b,size_t o,uint32_t v){for(int i=0;i<4;i++)b[o+i]=v>>(8*i);} void i64(std::vector& b,size_t o,int64_t v){for(int i=0;i<8;i++)b[o+i]=uint64_t(v)>>(8*i);} template void fp(std::vector& b,size_t o,T v){std::memcpy(b.data()+o,&v,sizeof v);} size_t a16(size_t x){return(x+15)&~size_t(15);} uint32_t crc(const std::vector& b){uint32_t c=~0u;for(size_t i=0;i=76&&i<80?0:b[i];c^=x;for(int j=0;j<8;j++)c=(c>>1)^((c&1)?0xedb88320u:0);}return~c;} struct P{uint16_t semantic=1,unit=1,group=1;uint8_t type=1,flags=1,vc=1,component=1;std::vector raw;}; std::vector fixture(uint32_t w=2,uint32_t h=2,std::vector

ps={}) { if(ps.empty()) ps={{1,1,1,1,1,1,1,{1,0,2,0,3,0,4,0}},{2,1,1,1,1,1,2,{5,0,6,0,7,0,8,0}},{3,0,0,6,1,3,0,{15}}}; if(w*h!=4) for(auto& p:ps){p.raw.assign(p.type==6?(w*h+7)/8:(p.type==1||p.type==2?w*h*2:p.type==5?w*h*4:w*h),0);if(p.type==6)std::fill(p.raw.begin(),p.raw.end(),0xff),p.raw.back()=uint8_t((1u<<((w*h-1)%8+1))-1);} size_t po=112+ps.size()*32, off=0; for(auto&p:ps){off=a16(off+p.raw.size());} std::vectorb(po+off);std::memcpy(b.data(),"QBV2",4);b[4]=2;b[5]=1;u16(b,6,999);b[8]=b[9]=b[10]=1;u16(b,12,ps.size());u32(b,16,w);u32(b,20,h);i64(b,24,100);i64(b,32,90);fp(b,40,-10.);fp(b,48,-10.);fp(b,56,10.);fp(b,64,10.);u32(b,72,off);u16(b,80,0xffff);size_t cur=0;for(size_t i=0;i& b){u32(b,76,crc(b));} particle::ingress::EncodedFieldRuntimeMetadata meta(){return{"field",1,7};} particle::ResolvedFieldProfileContract wind(){particle::ResolvedFieldProfileContract c; c.profileID="generic.wind";c.profileVersion=1;c.requiredPlanes={{1,particle::FieldValueClass::VelocityComponent,1,1,particle::FieldComponent::UEast,true},{2,particle::FieldValueClass::VelocityComponent,1,1,particle::FieldComponent::VNorth,true},{3,particle::FieldValueClass::Validity,0,0,particle::FieldComponent::None,true}};return c;} } TEST(ParticleQbinV2Decoder, DecodesGenericFrameAndSharesValidity) {auto r=particle::ingress::QbinV2Decoder::decode(meta(),fixture());ASSERT_TRUE(r.accepted())<referenceTimeMs,100000);EXPECT_EQ(r.frame->frameContentIdentity.validTimeMs,90000);EXPECT_EQ(r.frame->planes.size(),2);EXPECT_EQ(r.frame->validityPlane.storage,r.frame->validityBits);} TEST(ParticleQbinV2Decoder, RejectsStructuralAndCrcViolations) {auto b=fixture();b[0]='X';EXPECT_EQ(particle::ingress::QbinV2Decoder::decode(meta(),b).code,particle::ingress::QbinV2DecodeErrorCode::BadMagic);b=fixture();b[11]=1;recalc(b);EXPECT_EQ(particle::ingress::QbinV2Decoder::decode(meta(),b).code,particle::ingress::QbinV2DecodeErrorCode::ReservedNonZero);b=fixture();b[208]^=1;EXPECT_EQ(particle::ingress::QbinV2Decoder::decode(meta(),b).code,particle::ingress::QbinV2DecodeErrorCode::CrcMismatch);} TEST(ParticleQbinV2Decoder, RejectsValidityAndInvalidCells) {auto b=fixture();size_t po=208;b[po+32]=0x0e;recalc(b);EXPECT_EQ(particle::ingress::QbinV2Decoder::decode(meta(),b).code,particle::ingress::QbinV2DecodeErrorCode::InvalidCellPayload);b=fixture();b[po+32]=0xf0;recalc(b);EXPECT_EQ(particle::ingress::QbinV2Decoder::decode(meta(),b).code,particle::ingress::QbinV2DecodeErrorCode::InvalidValidity);} TEST(ParticleQbinV2Decoder, PlaneContentIdentityIsIndependent) {auto a=fixture();auto b=a;b[208+16]=9;recalc(b);auto ra=particle::ingress::QbinV2Decoder::decode(meta(),a);auto rb=particle::ingress::QbinV2Decoder::decode(meta(),b);ASSERT_TRUE(ra.accepted());ASSERT_TRUE(rb.accepted());EXPECT_EQ(ra.frame->planes[0].contentIdentity,rb.frame->planes[0].contentIdentity);EXPECT_NE(ra.frame->planes[1].contentIdentity,rb.frame->planes[1].contentIdentity);} TEST(ParticleQbinV2Decoder, DecodesLargeStructuralFixture) {auto r=particle::ingress::QbinV2Decoder::decode(meta(),fixture(513,769));ASSERT_TRUE(r.accepted())<grid.width,513);EXPECT_EQ(r.frame->grid.height,769);EXPECT_EQ(r.frame->planes[0].storage->size(),513u*769u*2u);} TEST(ParticleEncodedFieldIngress, ValidatesContractAndReportsMissing) {auto good=wind();auto ok=particle::ingress::EncodedFieldIngress::decodeAndValidate(meta(),&good,fixture());EXPECT_TRUE(ok.accepted());auto miss=particle::ingress::EncodedFieldIngress::decodeAndValidate(meta(),nullptr,fixture());EXPECT_EQ(miss.code,particle::ingress::EncodedFieldIngressCode::ContractMissing);auto c=wind();c.requiredPlanes.pop_back();auto bad=particle::ingress::EncodedFieldIngress::decodeAndValidate(meta(),&c,fixture());EXPECT_EQ(bad.code,particle::ingress::EncodedFieldIngressCode::ContractRejected);}