From 7ba9e1f3ee3c774aca498400619fa50fe38d11e1 Mon Sep 17 00:00:00 2001 From: DevmateUtgenCppGeneralFBOrg Bot Date: Tue, 21 Jul 2026 11:03:39 -0700 Subject: [PATCH] xplat/js/react-native-github/packages/react-native/ReactCommon/react/renderer/mounting/MountingTransaction.cpp (#57585) Summary: Pull Request resolved: https://github.com/react/react-native/pull/57585 Reviewed By: cortinico Differential Revision: D112079573 --- .../tests/MountingTransactionTest.cpp | 123 ++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 packages/react-native/ReactCommon/react/renderer/mounting/tests/MountingTransactionTest.cpp diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/tests/MountingTransactionTest.cpp b/packages/react-native/ReactCommon/react/renderer/mounting/tests/MountingTransactionTest.cpp new file mode 100644 index 00000000000..cdc8b1fe56e --- /dev/null +++ b/packages/react-native/ReactCommon/react/renderer/mounting/tests/MountingTransactionTest.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include + +#include +#include +#include +#include + +namespace facebook::react { + +namespace { + +ShadowView makeShadowView(Tag tag) { + ShadowView view; + view.tag = tag; + return view; +} + +MountingTransaction makeTransaction( + SurfaceId surfaceId, + MountingTransaction::Number number, + const std::vector& createTags, + int telemetryRevision = 0) { + ShadowViewMutationList mutations; + mutations.reserve(createTags.size()); + for (auto tag : createTags) { + mutations.push_back( + ShadowViewMutation::CreateMutation(makeShadowView(tag))); + } + TransactionTelemetry telemetry; + telemetry.setRevisionNumber(telemetryRevision); + return MountingTransaction{ + surfaceId, number, std::move(mutations), std::move(telemetry)}; +} + +} // namespace + +// Verifies that `mergeWith` concatenates mutations from the incoming +// transaction after the existing ones, preserving relative order across both. +// Catches regressions where the merged list is reversed, overwrites the +// original, or drops mutations from either side. +TEST(MountingTransactionTest, testMergeWithAppendsMutationsInOrder) { + auto transaction = makeTransaction( + /* surfaceId */ 1, /* number */ 1, /* tags */ {10, 11}); + auto incoming = makeTransaction( + /* surfaceId */ 1, /* number */ 2, /* tags */ {20, 21, 22}); + + transaction.mergeWith(std::move(incoming)); + + const auto& merged = transaction.getMutations(); + ASSERT_EQ(merged.size(), 5u); + EXPECT_EQ(merged[0].newChildShadowView.tag, 10); + EXPECT_EQ(merged[1].newChildShadowView.tag, 11); + EXPECT_EQ(merged[2].newChildShadowView.tag, 20); + EXPECT_EQ(merged[3].newChildShadowView.tag, 21); + EXPECT_EQ(merged[4].newChildShadowView.tag, 22); +} + +// Verifies that `mergeWith` advances `number` to that of the incoming +// transaction while leaving `surfaceId` untouched. Catches regressions where +// the number is not updated or the surfaceId is accidentally overwritten. +TEST(MountingTransactionTest, testMergeWithUpdatesNumberButPreservesSurfaceId) { + auto transaction = makeTransaction( + /* surfaceId */ 7, /* number */ 3, /* tags */ {100}); + auto incoming = makeTransaction( + /* surfaceId */ 7, /* number */ 42, /* tags */ {200}); + + transaction.mergeWith(std::move(incoming)); + + EXPECT_EQ(transaction.getSurfaceId(), 7); + EXPECT_EQ(transaction.getNumber(), 42); +} + +// Verifies that `mergeWith` replaces the existing telemetry with that of the +// incoming transaction (per the documented "use the latest instance" +// behavior). We use a distinguishing revision number so the assertion checks a +// computed value rather than restating a source constant. +TEST(MountingTransactionTest, testMergeWithAdoptsIncomingTelemetry) { + auto transaction = makeTransaction( + /* surfaceId */ 1, + /* number */ 1, + /* tags */ {}, + /* telemetryRevision */ 5); + auto incoming = makeTransaction( + /* surfaceId */ 1, + /* number */ 2, + /* tags */ {}, + /* telemetryRevision */ 99); + + transaction.mergeWith(std::move(incoming)); + + EXPECT_EQ(transaction.getTelemetry().getRevisionNumber(), 99); +} + +// Verifies that merging an empty incoming transaction is a no-op for the +// mutation list — the existing mutations remain intact, just with the number +// bumped. Guards against a naive `= std::move(other.mutations_)` implementation +// that would clobber the original list. +TEST( + MountingTransactionTest, + testMergeWithEmptyIncomingPreservesOriginalMutations) { + auto transaction = makeTransaction( + /* surfaceId */ 1, /* number */ 1, /* tags */ {10, 11, 12}); + auto incoming = makeTransaction( + /* surfaceId */ 1, /* number */ 2, /* tags */ {}); + + transaction.mergeWith(std::move(incoming)); + + const auto& merged = transaction.getMutations(); + ASSERT_EQ(merged.size(), 3u); + EXPECT_EQ(merged[0].newChildShadowView.tag, 10); + EXPECT_EQ(merged[1].newChildShadowView.tag, 11); + EXPECT_EQ(merged[2].newChildShadowView.tag, 12); + EXPECT_EQ(transaction.getNumber(), 2); +} + +} // namespace facebook::react