diff --git a/MobileGL/MG_Test/Buffer/SplitBufferTest.cpp b/MobileGL/MG_Test/Buffer/SplitBufferTest.cpp index 1a7f5470..a8fb9009 100644 --- a/MobileGL/MG_Test/Buffer/SplitBufferTest.cpp +++ b/MobileGL/MG_Test/Buffer/SplitBufferTest.cpp @@ -567,6 +567,19 @@ TEST_F(SplitBufferSet, TheAdoptTierIsSettledAtTheHandshakeAndNeverFatal) { EXPECT_EQ(Tier::SettleAdoptTierAtHandshake(false, Tier::AdoptTierSide::Server), 2u); EXPECT_TRUE(MG_Remote::Client::AdoptTierIsEmulate()); } + // UNSET (the default since 2026-09-29): a T0 ask of its own kind over shared segments, 2 over a + // stream, and a session-free caller runs T2 quietly instead of dying - it never named a tier. + MG_Config::Ipc.AdoptTier = MG_Config::kAdoptTierUnset; + Tier::ForgetAdoptTierSettlementForTest(); + EXPECT_TRUE(MG_Remote::Client::AdoptTierIsEmulate()); + EXPECT_EQ(Tier::AdoptTierAskFor(false), Tier::kAdoptAskT0Default); + EXPECT_EQ(Tier::AdoptTierAskFor(true), 2u); + EXPECT_EQ(Tier::SettleAdoptTierAtHandshake(false, Tier::AdoptTierSide::Client), Tier::kAdoptAskT0Default); + for (const auto side : {Tier::AdoptTierSide::Client, Tier::AdoptTierSide::Server}) { + Tier::ForgetAdoptTierSettlementForTest(); + EXPECT_EQ(Tier::SettleAdoptTierAtHandshake(true, side), 2u); + } + EXPECT_TRUE(MG_Remote::Client::AdoptTierIsEmulate()); MG_Config::Ipc.AdoptTier = 2; } diff --git a/MobileGL/MG_Test/Wire/AdoptTierStreamTest.cpp b/MobileGL/MG_Test/Wire/AdoptTierStreamTest.cpp index bd6ba207..f2b6bc1e 100644 --- a/MobileGL/MG_Test/Wire/AdoptTierStreamTest.cpp +++ b/MobileGL/MG_Test/Wire/AdoptTierStreamTest.cpp @@ -151,6 +151,33 @@ TEST(AdoptTierStream, AClientNamingT0OrT1IsRefusedOnceAndItsMapPersistentIsDecli } } +// P11 B2: THE KNOB UNSET - T0 BY DEFAULT since 2026-09-29 - ON A STREAM. The Hello asks 2 (a stream +// carries T2 only), the session runs T2, and nothing says so above MGLOG_D: every tcp session would +// otherwise carry A1's line. The client still COUNTS the fallback (ClientSession::T0Fallbacks, +// reported through PeerReport::pixel). The supervisor's own knob is unset as well (ServerSpawn +// scrubs it), so its stream settle is quiet too. +// RED-ONCE: log the unset stream settle at W instead of D -> one AdoptTierOnStream line, not 0. +TEST(AdoptTierStream, AnUnsetKnobRunsT2OnAStreamQuietlyAndCountsTheFallback) { + ServerProcess server; + ASSERT_TRUE(LaunchSupervisor("adopt-unset", &server)) << server.Log(); + const auto body = [](Remote::Client::ClientSession& client, PeerReport& r) { + MapPersistentThenARecord(client, r); + r.pixel = static_cast(client.T0Fallbacks()); + }; + const PeerReport r = RunPeer(server.endpoint, body, /*stopCleanly=*/true, 30000, 0, nullptr, + [] { MG_Config::Ipc.AdoptTier = MG_Config::kAdoptTierUnset; }); + const std::string client = ClientLog(server); + ASSERT_EQ(r.started, 1) << "the session did not come up: " << r.note << "\n" << client; + EXPECT_EQ(r.status, Remote::Wire::ReplySink::kStatusDeclined) << "a stream session runs T2\n" << client; + EXPECT_EQ(r.applied, 1) << client; + EXPECT_EQ(r.pixel, 1u) << "the stream's T0 fallback was not counted\n" << client; + for (const char* line : {"AdoptTierOnStream", "T0 by default", "AdoptT0Unavailable", "T0 session totals"}) { + EXPECT_EQ(Count(client, line), 0u) << "an unset knob's fallback is MGLOG_D only: " << line << "\n" << client; + EXPECT_EQ(Count(server.Log(), line), 0u) << line << "\n" << server.Log(); + } + EXPECT_EQ(Count(client, "Fatal{"), 0u) << client; +} + TEST(AdoptTierStream, AServerNamingT0RefusesOncePerSessionAndServesT2) { ServerProcess server; const std::string peerBase = "/tmp/mgl-p11-adopt-server-peers-" + std::to_string(::getpid()) + ".log"; diff --git a/MobileGL/MG_Test/Wire/RemoteClientAdoptT0.inc b/MobileGL/MG_Test/Wire/RemoteClientAdoptT0.inc index 01a2e1ce..a99a3d10 100644 --- a/MobileGL/MG_Test/Wire/RemoteClientAdoptT0.inc +++ b/MobileGL/MG_Test/Wire/RemoteClientAdoptT0.inc @@ -242,6 +242,36 @@ TEST(RemoteClientAdoptT0, KnobTwoIsTodaysT2WithNoT0Line) { EXPECT_EQ(child.Log.find("Fatal{"), std::string::npos) << child.Log; } +// THE KNOB UNSET - T0 BY DEFAULT, the user's decision of 2026-09-29 - over shared segments on the +// host's own platform (no AHardwareBuffer): the Hello asks T0 of its default kind, the server +// refuses it at its first bind and the client falls back at its first map_persistent - both +// QUIETLY (MGLOG_D only: every host session would otherwise carry two lines), both COUNTED. +// Knob 0's named lines are KnobZero... / NoAhardwareBuffer... above; this is the unset half. +// RED-ONCE: log the unset path's fallbacks at W instead of D -> the line counts below are 1, not 0. +TEST(RemoteClientAdoptT0, AnUnsetKnobFallsBackToT2QuietlyAndCountsIt) { + const auto child = RunInChild([] { + MG_Config::Ipc.AdoptTier = MG_Config::kAdoptTierUnset; + MG_Pipe::MGPipeSetResourceOps(&g_fakeOps); // a backend WITH the members: the platform decides + StartControlSession(); + if (!Srv::ServerSessionInstance().AdoptT0Asked()) ::_exit(210); // the Hello asked T0 by default + if (!Srv::ServerSessionInstance().AdoptT0Quiet()) ::_exit(211); + GrantAtFirstBind(); + if (Srv::ServerSessionInstance().AdoptT0()) ::_exit(212); + if (Srv::ServerSessionInstance().T0Refusals() != 1) ::_exit(213); // counted, not said + const auto seed = Pattern(kT0Bytes, 0x66); + const MGPHandleOnly handle = MakeBufferStore(5, kT0Bytes); + if (MGPipeRouteMapPersistent(handle, kT0Bytes, seed.data()) != nullptr) ::_exit(214); // T2: declined + if (ClientSessionInstance().AdoptT0()) ::_exit(215); + if (ClientSessionInstance().T0Fallbacks() != 1) ::_exit(216); // counted, not said + ClientSessionInstance().Stop(); + }); + ExpectChildSuccess(child); + for (const char* line : {"AdoptT0Unavailable", "T0 by default", "T0 POST", "T0 session totals", "Refuse{"}) + EXPECT_EQ(CountOf(child.Log, line), 0u) << "an unset knob's fallback is MGLOG_D only: " << line << "\n" + << child.Log; + EXPECT_EQ(child.Log.find("Fatal{"), std::string::npos) << child.Log; +} + // Knob 1 names T1, which P11 closed: one named refusal, the Hello asks T2, never a Fatal. TEST(RemoteClientAdoptT0, KnobOneIsRefusedByNameAndAsksT2) { const auto child = RunInChild([] { diff --git a/MobileGL/MG_Test/Wire/SessionHandshakeTest.cpp b/MobileGL/MG_Test/Wire/SessionHandshakeTest.cpp index 672d90de..33d01777 100644 --- a/MobileGL/MG_Test/Wire/SessionHandshakeTest.cpp +++ b/MobileGL/MG_Test/Wire/SessionHandshakeTest.cpp @@ -285,7 +285,8 @@ TEST(SessionHandshakeTest, ARevisionFourPeerIsRefusedByNameAtRevisionFive) { // legal ask is the Welcome's. TEST(SessionHandshakeTest, AHelloAskingAnAdoptTierThatIsNotAnAskIsRefusedByName) { using namespace ::MobileGL::Wire; - for (const Uint8 ask : {Uint8{0}, Uint8{1}, Uint8{2}, Uint8{7}}) { + // 0x80 is the T0 ask of an unset knob (T0 by default, refusals quiet); 0x81 is not an ask. + for (const Uint8 ask : {Uint8{0}, Uint8{1}, Uint8{2}, Uint8{7}, Uint8{0x80}, Uint8{0x81}}) { SCOPED_TRACE(static_cast(ask)); std::unique_ptr client, server; Transport::InProcessTransport::CreatePair(client, server); @@ -302,7 +303,7 @@ TEST(SessionHandshakeTest, AHelloAskingAnAdoptTierThatIsNotAnAskIsRefusedByName) sizes.CmdRingBytes = 4096; sizes.StageBytes = 4096; sizes.ReplyBytes = 4096; sizes.EventRingBytes = 4096; session.SetSegmentSizes(sizes); - const Bool legal = ask == 0 || ask == 2; + const Bool legal = ask == 0 || ask == 2 || ask == 0x80; EXPECT_EQ(session.Accept(*server, &first), legal ? MOBILEGL_OK : MOBILEGL_ERR_PROTOCOL_MISMATCH); const auto reply = ReadHandshakeFrame(*client); ASSERT_FALSE(reply.empty()); @@ -311,7 +312,8 @@ TEST(SessionHandshakeTest, AHelloAskingAnAdoptTierThatIsNotAnAskIsRefusedByName) ASSERT_NE(envelope->msg_as_Welcome(), nullptr); ASSERT_NE(envelope->msg_as_Welcome()->linkTerms(), nullptr); EXPECT_EQ(envelope->msg_as_Welcome()->linkTerms()->adoptTier(), ask) << "the Welcome echoes the ask"; - EXPECT_EQ(session.AdoptT0Asked(), ask == 0); + EXPECT_EQ(session.AdoptT0Asked(), ask == 0 || ask == 0x80); + EXPECT_EQ(session.AdoptT0Quiet(), ask == 0x80) << "only the default ask is quiet"; EXPECT_FALSE(session.AdoptT0()) << "no grant before the first native bind"; } else { ASSERT_NE(envelope->msg_as_Refuse(), nullptr);