From 6a07afb02246dadd8256506c42f1a5911b0e6c22 Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Sun, 16 Aug 2026 15:02:07 -0500 Subject: [PATCH 1/9] update - ci Adding an additional unified hybrid spawning test pass. --- .yamato/_run-all.yml | 15 ++++++++ .yamato/_triggers.yml | 24 ++++++++++++ .yamato/project.metafile | 36 ++++++++++++++++++ .yamato/unified-tests.yml | 78 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 153 insertions(+) create mode 100644 .yamato/unified-tests.yml diff --git a/.yamato/_run-all.yml b/.yamato/_run-all.yml index 6cc1709b16..d0af1a4d4f 100644 --- a/.yamato/_run-all.yml +++ b/.yamato/_run-all.yml @@ -368,6 +368,21 @@ run_all_project_tests_console_standalone_default: {% endfor -%} +# Runs all unified (NGO + N4E) tests +# These run on their own pinned editor (unified_editors) rather than the validation_editors, because +# they need an editor that bundles a com.unity.netcode with the unified API. See unified-tests.yml. +run_all_unified_tests: + name: Run All Unified Tests + dependencies: +{% for project in projects.default -%} +{% for platform in unified_test_platforms -%} +{% for editor in unified_editors.default -%} + - .yamato/unified-tests.yml#unified_test_{{ project.name }}_{{ platform.name }}_{{ editor }} +{% endfor -%} +{% endfor -%} +{% endfor -%} + + # Runs all CMB service tests run_all_project_tests_cmb_service: name: Run All CMB Service Tests diff --git a/.yamato/_triggers.yml b/.yamato/_triggers.yml index ac4356cd7a..5d8ca30e88 100644 --- a/.yamato/_triggers.yml +++ b/.yamato/_triggers.yml @@ -90,6 +90,13 @@ pr_code_changes_checks: # Note that our daily tests will anyway run both test configurations in "minimal supported" and "trunk" configurations - .yamato/desktop-standalone-tests.yml#desktop_standalone_test_testproject_ubuntu_il2cpp_{{ pinnedTrunk }} - .yamato/cmb-service-standalone-tests.yml#cmb_service_standalone_test_testproject_ubuntu_il2cpp_{{ pinnedTrunk }} + + # Run the unified (NGO + N4E) tests. Unlike every other job here this one runs on a pinned Unity + # alpha (unified_editors in project.metafile) rather than a supported editor, because it needs an + # editor that bundles a com.unity.netcode with the unified API. Expect it to need a pin bump + # whenever N4E lands breaking changes in trunk. See .yamato/unified-tests.yml. + - .yamato/_run-all.yml#run_all_unified_tests + # Run code coverage test (PRs use the pinned "safe" trunk) - .yamato/code-coverage.yml#code_coverage_project_test_testproject_ubuntu_{{ pinnedTrunk }} triggers: @@ -120,6 +127,21 @@ pr_code_changes_checks: +# Unified (NGO + N4E) validation, on demand. +# The unified tests run automatically as part of pr_code_changes_checks and develop_nightly (same as +# the CMB service tests do). This job only exists so they can also be kicked off by commenting +# "/ci unified", for PRs that pr_code_changes_checks does not cover (drafts, doc-only changes, or +# branches targeting something other than develop / develop-3.x.x / release). +unified_pr_checks: + name: Unified (NGO + N4E) checks [on demand] + dependencies: + - .yamato/_run-all.yml#run_all_unified_tests + triggers: + expression: |- + pull_request.comment eq "unified" + cancel_old_ci: true + + # Run all tests on nightly basis. # Same subset as pull_request_trigger with addition of mobile/desktop/console tests and webgl builds # Those tests are all running on trunk and the default editor (since it's daily and running all of them would add a lot of overhead) @@ -156,6 +178,8 @@ develop_nightly: # Run Runtime tests against cmb service on trunk and default editors - .yamato/_run-all.yml#run_all_project_tests_cmb_service_trunk - .yamato/_run-all.yml#run_all_project_tests_cmb_service_default + # Run the unified (NGO + N4E) tests on their own pinned editor (see .yamato/unified-tests.yml) + - .yamato/_run-all.yml#run_all_unified_tests # Build player for webgl platform on trunk and default editors - .yamato/project-updated-dependencies-test.yml#updated-dependencies_testproject_NGO_ubuntu_trunk - .yamato/project-updated-dependencies-test.yml#updated-dependencies_testproject_NGO_win_{{ validation_editors.default }} diff --git a/.yamato/project.metafile b/.yamato/project.metafile index f1e5c5c924..38ff5ed3d7 100644 --- a/.yamato/project.metafile +++ b/.yamato/project.metafile @@ -190,6 +190,42 @@ validation_editors: pinnedTrunk: 5fe7931aab8c4fff9274e15ef0800125c68b8d6a +# UNIFIED (NGO + N4E) CONFIGURATION--------------------------------------------------------------------- +# The unified test job validates NGO running against Netcode for Entities (N4E) through the unified API. +# It is deliberately kept separate from every other job in this repo because it needs an editor that +# bundles com.unity.netcode with the unified API, and that editor is NOT one of the validation_editors. +# +# WHY THIS IS PINNED TO 6000.7.0a5 (and not 6000.7.0a2): +# UnifiedNetcodeTransport is compiled behind "#if UNIFIED_NETCODE && OUT_OF_BAND_RPC" and needs the +# N4E out-of-band RPC API (IOutOfBandRpcCommand / OutgoingOutOfBandRpcDataStreamBuffer). +# Both 6000.7.0a2 and 6000.7.0a5 bundle com.unity.netcode 6.7.0, but that API only landed in the a5 +# snapshot. On a2 the unified transport does not compile, so the unified tests cannot run there. +# Bump this pin (and unified_netcode_version below) together whenever the required N4E API moves. +# +# An explicit alpha version is used instead of a trunk revision hash because published alphas are +# already immutable, so there is nothing to pin against. +unified_editors: + default: + - 6000.7.0a5 + +# Version of com.unity.netcode (N4E) bundled with unified_editors.default. +# It resolves as a "builtin" package out of the editor install, so it must match the editor exactly. +unified_netcode_version: 6.7.0 + +# The unified job runs in Editor context only, so a single fast platform is enough. +unified_test_platforms: + - name: ubuntu + type: Unity::VM + image: package-ci/ubuntu-22.04:v4.87.0 + flavor: b1.large + +# Restricts the unified job to unified-only tests so nothing else runs on the alpha editor. +# The pattern matches on the NUnit full test name, which covers both the dedicated unified fixtures +# (UnifiedNetworkTransformTest) and any shared fixture parameterized with +# HostOrServer.UnifiedHost / HostOrServer.UnifiedServer, e.g. "NetworkTransformTests(UnifiedHost,...)". +unified_test_filter: "*Unified*" + + # Scripting backends used by Standalone RunTimeTests--------------------------------------------------- scripting_backends: diff --git a/.yamato/unified-tests.yml b/.yamato/unified-tests.yml new file mode 100644 index 0000000000..9e4e6b315e --- /dev/null +++ b/.yamato/unified-tests.yml @@ -0,0 +1,78 @@ +{% metadata_file .yamato/project.metafile %} # All configuration that is used to create different configurations (used in for loops) is taken from this file. +--- + +# DESCRIPTION-------------------------------------------------------------------------- + # This job validates NGO running against Netcode for Entities (N4E) through the unified API. + # A "hybrid prefab" is an NGO prefab (NetworkObject at the root) that also carries a GhostObject. + # When one or more hybrid prefabs are in the NetworkManager's prefab list, NGO hands transform + # synchronization to N4E's snapshot system and tunnels its own batched messages over + # UnifiedNetcodeTransport (N4E's out-of-band RPC) instead of using a NetworkTransport such as UTP. + # UnifiedNetworkTransformTest is the validation test that this path works end to end. + +# WHY THIS JOB IS SEPARATE FROM EVERY OTHER JOB------------------------------------------ + # 1. It needs an editor that bundles a com.unity.netcode with the unified API. That editor + # (unified_editors.default in project.metafile) is not one of the validation_editors, and NGO + # still has to keep building and testing against editors that have no unified API at all. + # 2. It needs com.unity.netcode in the testproject so that the UNIFIED_NETCODE define is set + # (see the versionDefines in Unity.Netcode.Runtime.asmdef). The committed + # testproject/Packages/manifest.json deliberately does NOT reference it, so this job swaps in + # testproject/Packages/manifest-unified.json instead. + # 3. It is wired into pr_code_changes_checks and develop_nightly the same way the CMB service tests + # are, so it runs automatically on PRs targeting develop / develop-3.x.x / release. Be aware that + # this makes an unsupported alpha editor part of the PR gate: when N4E lands breaking changes in + # trunk this job goes red and the pin has to be bumped to unblock PRs. + # _triggers.yml also has unified_pr_checks, so it can be kicked off with "/ci unified" on PRs + # that pr_code_changes_checks does not cover. + +# CONFIGURATION STRUCTURE-------------------------------------------------------------- + # Jobs are generated using nested loops: + # 1. For all unified test platforms (currently Ubuntu only, see project.metafile) + # 2. For all unified editors (currently a single pinned alpha, see project.metafile) + +# TECHNICAL CONSIDERATIONS--------------------------------------------------------------- + # This job runs in Editor context only (no player builds required), like project-tests.yml. + # Only playmode is run: every unified test is an integration test and there are no unified EditMode tests. + # The run is restricted to unified tests via unified_test_filter so that nothing else is exercised + # on the alpha editor. Non-unified tests are covered by the regular jobs on the supported editors. + # packages-lock.json is removed because the committed lock was resolved against a much older editor + # and its builtin package versions do not exist in the unified editor. + +# QUALITY CONSIDERATIONS-------------------------------------------------------------------- + # TODO: the manifest swap means testproject/Packages/manifest-unified.json has to be kept in sync + # with manifest.json by hand. Revisit once N4E is a hard dependency of NGO and one manifest + # can cover both cases. + # TODO: unified_test_filter is a test-name pattern. Replacing it with an NUnit category (for + # example [Category("Unified")]) would be less fragile once more fixtures gain unified variants. + +#------------------------------------------------------------------------------------ + +{% for project in projects.default -%} +{% for platform in unified_test_platforms -%} +{% for editor in unified_editors.default -%} +unified_test_{{ project.name }}_{{ platform.name }}_{{ editor }}: + name : Unified Test - NGO {{ project.name }} [{{ platform.name }}, {{ editor }}] + agent: + type: {{ platform.type }} + image: {{ platform.image }} + flavor: {{ platform.flavor }} +{% if platform.model %} + model: {{ platform.model }} # This is set only in platforms where we want non-default model to use (more information in project.metafile) +{% endif %} + commands: + # Swap in the manifest that pulls in com.unity.netcode (N4E). This is what causes UNIFIED_NETCODE + # to be defined and therefore what makes the unified tests compile at all. + - cp {{ project.path }}/Packages/manifest-unified.json {{ project.path }}/Packages/manifest.json + # The committed lock was resolved against a supported editor, not the unified alpha. + - rm -f {{ project.path }}/Packages/packages-lock.json + + - unity-downloader-cli --fast --wait -u {{ editor }} -c Editor # Installing basic editor for tests execution + - UnifiedTestRunner --testproject={{ project.path }} --suite=playmode --artifacts-path=test-results --editor-location=.Editor --testfilter="{{ unified_test_filter }}" --rerun-strategy=Test --retry={{ num_test_retries }} --clean-library-on-rerun --timeout={{ test_timeout }} + artifacts: + logs: + paths: + - "test-results/**/*" + dependencies: + - .yamato/_run-all.yml#run_quick_checks # initial checks to perform fast validation of common errors +{% endfor -%} +{% endfor -%} +{% endfor -%} From b0c8a3caa76a8fb2b6d457016b2911be971bafda Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Sun, 16 Aug 2026 15:11:43 -0500 Subject: [PATCH 2/9] fix Fixed: - Issue with NetworkObject throwing an exception during hybrid integration test. - Issue with setting the active world prior to spawning a hybrid prefab during integration tests. - Issue with UnifiedNetcodeUpdateSystem not overriding the OnCreate method along with checks for a valid NetworkManager and/or transport within OnUpdate. - Issues (minor) with the original UnifiedNetworkTransformTest. - Adjustments to the NetcodeIntegrationTest that assures the correct active world is assigned when spawning. --- .../Runtime/Core/NetworkObject.cs | 11 +++++ .../Unified/UnifiedNetcodeTransport.cs | 22 ++++++++-- .../UnifiedNetworkTransformTest.cs | 40 +++++++++++-------- .../TestHelpers/NetcodeIntegrationTest.cs | 22 +++++++--- 4 files changed, 70 insertions(+), 25 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs index 306cbf4572..94364a217a 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs @@ -3968,6 +3968,17 @@ private void Start() private void InitGhost() { + // Note: If hybrid prefabs are created prior to any NetworkManager instances, + // then the next line throws and exception. This avoids that issue. + // We might come up with some global way to verify if we are running integration + // tests and add additional logic within to determine if we should log an error + // or not. + if (NetworkManager == null) + { + return; + } + + if (!NetworkManager.IsListening) { if (NetworkManager.LogLevel == LogLevel.Developer) diff --git a/com.unity.netcode.gameobjects/Runtime/Transports/Unified/UnifiedNetcodeTransport.cs b/com.unity.netcode.gameobjects/Runtime/Transports/Unified/UnifiedNetcodeTransport.cs index bdaebdc3ca..785c959da9 100644 --- a/com.unity.netcode.gameobjects/Runtime/Transports/Unified/UnifiedNetcodeTransport.cs +++ b/com.unity.netcode.gameobjects/Runtime/Transports/Unified/UnifiedNetcodeTransport.cs @@ -124,10 +124,18 @@ public void OnUpdate(ref SystemState state) [UpdateBefore(typeof(RpcSystem))] internal partial class UnifiedNetcodeUpdateSystem : SystemBase { - public void OnCreate(ref SystemState state) + // Note: this must be the parameterless ComponentSystemBase.OnCreate override, not the + // ISystem-style OnCreate(ref SystemState). With the ISystem signature this is just a new + // method that Entities never calls, so neither RequireForUpdate takes effect and OnUpdate + // runs from the very first world tick - which is before StartClient/StartServer have + // assigned Transport and NetworkManager below, and therefore a NullReferenceException. + // CreateSingleWorldHost calls AppendWorldToCurrentPlayerLoop, so the world can tick in the + // window between world creation and the transport being started. + protected override void OnCreate() { - state.RequireForUpdate(); - state.RequireForUpdate(); + RequireForUpdate(); + RequireForUpdate(); + base.OnCreate(); } public UnifiedNetcodeTransport Transport; @@ -155,6 +163,14 @@ public void SendRpc(TransportRpc rpc, Entity connectionEntity) protected override void OnUpdate() { + // Belt and braces alongside the RequireForUpdate gating in OnCreate: these are only + // assigned once the transport is started but the world can already be in the player loop + // before that happens. Exit early under this scenario. + if (NetworkManager == null || Transport == null) + { + return; + } + NetworkManager.MessageManager.ProcessSendQueues(); using var commandBuffer = new EntityCommandBuffer(Allocator.Temp); diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/UnifiedNetworkTransformTest.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/UnifiedNetworkTransformTest.cs index b5fd31ae71..fc128ae31f 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/UnifiedNetworkTransformTest.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/UnifiedNetworkTransformTest.cs @@ -1,5 +1,6 @@ #if UNIFIED_NETCODE using System.Collections; +using System.Text; using NUnit.Framework; using Unity.Netcode.Components; using Unity.Netcode.TestHelpers.Runtime; @@ -52,38 +53,45 @@ protected override IEnumerator OnSetup() return base.OnSetup(); } + private bool ValidatePosition(StringBuilder errorLog) + { + var authorityPosition = m_AuthorityInstanceTransform.position; + foreach (var client in m_ClientNetworkManagers) + { + var instancePosition = client.SpawnManager.SpawnedObjects[m_Instance.NetworkObjectId].transform.position; + if (!Approximately(authorityPosition, instancePosition)) + { + errorLog.AppendLine($"[Client-{client.LocalClientId}] Clone instance position {GetVector3Values(instancePosition)} " + + $"does not equal the authority's {GetVector3Values(authorityPosition)}"); + } + } + return errorLog.Length == 0; + } + + private Transform m_AuthorityInstanceTransform; + [UnityTest] public IEnumerator BasicMovementTest() { var authority = GetAuthorityNetworkManager(); m_Instance = SpawnObject(m_Prefab, m_ServerNetworkManager).GetComponent(); - - // Wait 5 seconds so we will dump any deferred messages if it failed on clients - // when checking to see if it spawned or not on the clients next. - // Enable this to debug deferred - //yield return new WaitForSeconds(5); + m_AuthorityInstanceTransform = m_Instance.transform; yield return WaitForSpawnedOnAllOrTimeOut(m_Instance); AssertOnTimeout($"Failed to spawn {m_Instance.name} on all clients!"); VerboseDebug("All clients spawned instance!"); + yield return WaitForConditionOrTimeOut(ValidatePosition); + AssertOnTimeout($"A client failed to synchronize the position on the initial spawn!"); + var originalPos = authority.LocalClient.PlayerObject.transform.position; var newPos = originalPos + new Vector3(1, 1, 1); m_Instance.transform.position = newPos; - foreach (var client in m_ClientNetworkManagers) - { - Assert.IsTrue(Approximately(originalPos, s_GlobalNetworkObjects[client.LocalClientId][m_Instance.NetworkObjectId].transform.position)); - } - - yield return new WaitForSeconds(1); - - foreach (var client in m_ClientNetworkManagers) - { - Assert.IsTrue(Approximately(newPos, s_GlobalNetworkObjects[client.LocalClientId][m_Instance.NetworkObjectId].transform.position)); - } + yield return WaitForConditionOrTimeOut(ValidatePosition); + AssertOnTimeout($"A client failed to synchronize the changed position: {GetVector3Values(newPos)}!"); VerboseDebug("Test Passed!"); } } diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs index 51d91eea3d..8cfd641c33 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs @@ -2436,13 +2436,9 @@ protected GameObject CreateHybridPrefab(string baseName, bool moveToDDOL = true) // - Then set it back to active. gameObject.SetActive(false); var adapter = gameObject.AddComponent(); - // Mark the reference as post processing to avoid registering this instance automatically. - GhostPrefabReference.s_IsPostProcessing = true; - adapter.prefabReference = ScriptableObject.CreateInstance(); - adapter.prefabReference.name = "GhostPrefabReference"; - adapter.prefabReference.Prefab = gameObject; - GhostPrefabReference.s_IsPostProcessing = false; + // Initialize it as a prefab + adapter.InitializeAsPrefab(); // TODO: This might be part of the CreateHybridPrefab parameters // For now, just use normal interpolation until we get integration @@ -2610,6 +2606,20 @@ protected void SpawnObjectInstance(NetworkObject networkObjectToSpawn, NetworkMa private GameObject SpawnObject(NetworkObject prefabNetworkObject, NetworkManager owner, bool destroyWithScene = false, bool isPlayerObject = false) { Assert.IsTrue(prefabNetworkObject.GlobalObjectIdHash > 0, $"{nameof(GameObject)} {prefabNetworkObject.name} has a {nameof(NetworkObject.GlobalObjectIdHash)} value of 0! Make sure to make it a valid prefab before trying to spawn!"); +#if UNIFIED_NETCODE + // This has to happen *before* Instantiate, not after. The hybrid prefab is active, so the clone's + // GhostObject.Awake runs synchronously inside Object.Instantiate below. The clone is not a prefab + // (its prefabReference.Prefab points at the prefab, not at itself), so Awake acquires an entity + // reference, which resolves the world to spawn into from the Netcode.Instance.m_ActiveWorld singleton. + // N4E's rate managers reassign that singleton on every world update, so by the time a test body runs + // it points at whichever world updated last - typically a client world - and the spawn is rejected with + // "You can only spawn a ghost on a server or during prediction on a client." + // TODO-FixMe: NetCode.Netcode.Instance is a singleton and might cause issues assigning this. + if (prefabNetworkObject.HasGhost) + { + NetCode.Netcode.Instance.m_ActiveWorld = m_ServerNetworkManager.NetcodeWorld; + } +#endif var newInstance = Object.Instantiate(prefabNetworkObject.gameObject); var networkObjectToSpawn = newInstance.GetComponent(); SpawnObjectInstance(networkObjectToSpawn, owner, destroyWithScene, isPlayerObject); From 75472691461420f45c20d1600a6d3baeb1865c6b Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Sun, 16 Aug 2026 15:13:18 -0500 Subject: [PATCH 3/9] update Adding the modified NetcodeConfig specific to NGO. Adding the unified manifest file for running unified tests. --- testproject/Assets/NetCodeConfig.asset | 28 ++++++---- testproject/Packages/manifest-unified.json | 61 ++++++++++++++++++++++ 2 files changed, 78 insertions(+), 11 deletions(-) create mode 100644 testproject/Packages/manifest-unified.json diff --git a/testproject/Assets/NetCodeConfig.asset b/testproject/Assets/NetCodeConfig.asset index f6b1280970..8988bbd48c 100644 --- a/testproject/Assets/NetCodeConfig.asset +++ b/testproject/Assets/NetCodeConfig.asset @@ -18,7 +18,7 @@ MonoBehaviour: ClientServerTickRate: SimulationTickRate: 30 PredictedFixedStepSimulationTickRatio: 1 - NetworkTickRate: 0 + NetworkTickRate: 30 MaxSimulationStepsPerFrame: 1 MaxSimulationStepBatchSize: 4 TargetFrameRateMode: 0 @@ -27,8 +27,8 @@ MonoBehaviour: m_ClampPartialTicksThreshold: 5 HandshakeApprovalTimeoutMS: 5000 ClientTickRate: - InterpolationTimeNetTicks: 2 - InterpolationTimeMS: 0 + InterpolationTimeNetTicks: 0 + InterpolationTimeMS: 50 MaxExtrapolationTimeSimTicks: 20 ForcedInputLatencyTicks: 0 MaxPredictAheadTimeMS: 500 @@ -38,18 +38,19 @@ MonoBehaviour: NumAdditionalCommandsToSend: 2 MaxPredictionStepBatchSizeRepeatedTick: 0 MaxPredictionStepBatchSizeFirstTimeTick: 0 + AlwaysRollbackAllPredictedGhosts: 0 PredictionLoopUpdateMode: 0 InterpolationDelayJitterScale: 1.25 - InterpolationDelayMaxDeltaTicksFraction: 0.1 + InterpolationDelayMaxDeltaTicksFraction: 0.15 InterpolationDelayCorrectionFraction: 0.1 - InterpolationTimeScaleMin: 0.85 - InterpolationTimeScaleMax: 1.1 + InterpolationTimeScaleMin: 0.9 + InterpolationTimeScaleMax: 1.33 CommandAgeCorrectionFraction: 0.1 PredictionTimeScaleMin: 0.9 PredictionTimeScaleMax: 1.1 GhostSendSystemData: - DefaultSnapshotPacketSize: 0 - PercentReservedForDespawnMessages: 0.33 + DefaultSnapshotPacketSize: 15000 + PercentReservedForDespawnMessages: 0.2 MinSendImportance: 0 MinDistanceScaledSendImportance: 0 MaxIterateChunks: 0 @@ -62,15 +63,20 @@ MonoBehaviour: CleanupConnectionStatePerTick: 1 m_FirstSendImportanceMultiplier: 1 m_IrrelevantImportanceDownScale: 1 - m_TempStreamSize: 8192 + m_TempStreamSize: 4192 m_UseCustomSerializer: 0 ConnectTimeoutMS: 1000 MaxConnectAttempts: 60 DisconnectTimeoutMS: 30000 HeartbeatTimeoutMS: 500 ReconnectionTimeoutMS: 2000 - ClientSendQueueCapacity: 64 - ClientReceiveQueueCapacity: 64 + ClientSendQueueCapacity: 128 + ClientReceiveQueueCapacity: 128 ServerSendQueueCapacity: 512 ServerReceiveQueueCapacity: 512 MaxMessageSize: 1400 + TracingConfig: + TracingMemoryLimitMb: 2048 + FuzzyFactor: 0.0001 + IgnoreDTDiffs: 0 + IgnorePartialTicks: 0 diff --git a/testproject/Packages/manifest-unified.json b/testproject/Packages/manifest-unified.json new file mode 100644 index 0000000000..faaad41358 --- /dev/null +++ b/testproject/Packages/manifest-unified.json @@ -0,0 +1,61 @@ +{ + "disableProjectUpdate": false, + "dependencies": { + "com.unity.addressables": "4.0.1", + "com.unity.ai.navigation": "2.0.14", + "com.unity.collab-proxy": "2.13.6", + "com.unity.ide.rider": "3.0.40", + "com.unity.ide.visualstudio": "2.0.26", + "com.unity.mathematics": "1.4.0", + "com.unity.multiplayer.tools": "2.2.10", + "com.unity.netcode": "6.7.0", + "com.unity.netcode.gameobjects": "file:../../com.unity.netcode.gameobjects", + "com.unity.package-validation-suite": "0.49.0-preview", + "com.unity.services.authentication": "3.7.3", + "com.unity.services.multiplayer": "2.3.0", + "com.unity.test-framework": "1.8.0", + "com.unity.test-framework.performance": "6.6.0", + "com.unity.timeline": "6.7.0", + "com.unity.ugui": "2.7.0", + "com.unity.modules.accessibility": "1.0.0", + "com.unity.modules.adaptiveperformance": "1.0.0", + "com.unity.modules.ai": "1.0.0", + "com.unity.modules.androidjni": "1.0.0", + "com.unity.modules.animation": "1.0.0", + "com.unity.modules.assetbundle": "1.0.0", + "com.unity.modules.audio": "1.0.0", + "com.unity.modules.cloth": "1.0.0", + "com.unity.modules.director": "1.0.0", + "com.unity.modules.imageconversion": "1.0.0", + "com.unity.modules.imgui": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0", + "com.unity.modules.particlesystem": "1.0.0", + "com.unity.modules.physics": "1.0.0", + "com.unity.modules.physics2d": "1.0.0", + "com.unity.modules.physicscore2d": "1.0.0", + "com.unity.modules.screencapture": "1.0.0", + "com.unity.modules.smartstrings": "1.0.0", + "com.unity.modules.terrain": "1.0.0", + "com.unity.modules.terrainphysics": "1.0.0", + "com.unity.modules.tetgen": "1.0.0", + "com.unity.modules.tilemap": "1.0.0", + "com.unity.modules.timelinefoundation": "1.0.0", + "com.unity.modules.ui": "1.0.0", + "com.unity.modules.uielements": "1.0.0", + "com.unity.modules.umbra": "1.0.0", + "com.unity.modules.unityanalytics": "1.0.0", + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.unitywebrequestassetbundle": "1.0.0", + "com.unity.modules.unitywebrequestaudio": "1.0.0", + "com.unity.modules.unitywebrequesttexture": "1.0.0", + "com.unity.modules.unitywebrequestwww": "1.0.0", + "com.unity.modules.vectorgraphics": "1.0.0", + "com.unity.modules.vehicles": "1.0.0", + "com.unity.modules.video": "1.0.0", + "com.unity.modules.wind": "1.0.0", + "com.unity.modules.xr": "1.0.0" + }, + "testables": [ + "com.unity.netcode.gameobjects" + ] +} From 319d7eb4470c0a72fa8cd34f8a490a988ba7efdf Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Sun, 16 Aug 2026 15:18:36 -0500 Subject: [PATCH 4/9] update Unified specific settings --- .../EntitiesClientSettings.asset | 16 +++++++++ .../NetCodeClientAndServerSettings.asset | 35 +++++++++++++++++++ .../NetCodeServerSettings.asset | 17 +++++++++ 3 files changed, 68 insertions(+) create mode 100644 testproject/ProjectSettings/EntitiesClientSettings.asset create mode 100644 testproject/ProjectSettings/NetCodeClientAndServerSettings.asset create mode 100644 testproject/ProjectSettings/NetCodeServerSettings.asset diff --git a/testproject/ProjectSettings/EntitiesClientSettings.asset b/testproject/ProjectSettings/EntitiesClientSettings.asset new file mode 100644 index 0000000000..baf6668fde --- /dev/null +++ b/testproject/ProjectSettings/EntitiesClientSettings.asset @@ -0,0 +1,16 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &1 +MonoBehaviour: + m_ObjectHideFlags: 53 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: e2ea235c1fcfe29488ed97c467a0da53, type: 3} + m_Name: + m_EditorClassIdentifier: Unity.Entities.Build::Unity.Entities.Build.EntitiesClientSettings + FilterSettings: + ExcludedBakingSystemAssemblies: [] diff --git a/testproject/ProjectSettings/NetCodeClientAndServerSettings.asset b/testproject/ProjectSettings/NetCodeClientAndServerSettings.asset new file mode 100644 index 0000000000..09eae04db5 --- /dev/null +++ b/testproject/ProjectSettings/NetCodeClientAndServerSettings.asset @@ -0,0 +1,35 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &1 +MonoBehaviour: + m_ObjectHideFlags: 53 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4111970df3904877aade1a474116e5c2, type: 3} + m_Name: + m_EditorClassIdentifier: Unity.NetCode.Authoring.Hybrid::Unity.NetCode.Hybrid.NetCodeClientAndServerSettings + FilterSettings: + ExcludedBakingSystemAssemblies: [] + AdditionalScriptingDefines: [] + GlobalNetCodeConfig: {fileID: 11400000, guid: c547acbddd81d32a0ba5e62ddfc4f4e3, type: 2} + CurrentImportanceSuggestions: + - MinValue: 1 + MaxValue: 4 + Name: Low Importance + Tooltip: For cosmetic (i.e. visual-only) ghosts like glass bottles, signs, beach-balls, and cones etc. Typically Static. + - MinValue: 5 + MaxValue: 40 + Name: Medium Importance + Tooltip: For common gameplay-affecting ghosts like trees, doors, explosive barrels, dropped loot etc. Typically Static. + - MinValue: 50 + MaxValue: 250 + Name: High Importance + Tooltip: For per-player and objective-critical ghosts like Player Character Controllers and CTF flags etc. Typically for Dynamic i.e. Predicted ghosts. UsePreSerialization is likely a good fit. + - MinValue: 1000 + MaxValue: 0 + Name: Critical Importance + Tooltip: For gameplay critical singletons like the one keeping the current score, or the one denoting whether or not the current round has started etc. Choose UsePreSerialization, and use sparingly. diff --git a/testproject/ProjectSettings/NetCodeServerSettings.asset b/testproject/ProjectSettings/NetCodeServerSettings.asset new file mode 100644 index 0000000000..a53b8eef71 --- /dev/null +++ b/testproject/ProjectSettings/NetCodeServerSettings.asset @@ -0,0 +1,17 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &1 +MonoBehaviour: + m_ObjectHideFlags: 53 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 70bae3f5df04479cba47f4cd56a60d53, type: 3} + m_Name: + m_EditorClassIdentifier: Unity.NetCode.Authoring.Hybrid::Unity.NetCode.Hybrid.NetCodeServerSettings + FilterSettings: + ExcludedBakingSystemAssemblies: [] + AdditionalScriptingDefines: [] From 9e8b6bb2ac03ea2d7704b0445999a8bb605c1563 Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Sun, 16 Aug 2026 15:51:04 -0500 Subject: [PATCH 5/9] fix Formatting of the unified test filter. --- .yamato/project.metafile | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/.yamato/project.metafile b/.yamato/project.metafile index 38ff5ed3d7..eb6c88d70b 100644 --- a/.yamato/project.metafile +++ b/.yamato/project.metafile @@ -220,10 +220,17 @@ unified_test_platforms: flavor: b1.large # Restricts the unified job to unified-only tests so nothing else runs on the alpha editor. -# The pattern matches on the NUnit full test name, which covers both the dedicated unified fixtures -# (UnifiedNetworkTransformTest) and any shared fixture parameterized with -# HostOrServer.UnifiedHost / HostOrServer.UnifiedServer, e.g. "NetworkTransformTests(UnifiedHost,...)". -unified_test_filter: "*Unified*" +# +# This is a REGULAR EXPRESSION, not a glob. UTR passes it to the editor as -testFilter, which ends up +# in UnityEngine.TestRunner's FullNameFilter -> NUnit ValueMatchFilter -> new Regex(pattern).IsMatch(). +# A glob-style "*Unified*" throws "Quantifier {x,y} following nothing" and fails the whole run. +# The other jobs' "Unity.Netcode.RuntimeTests.*" works because it is also a valid regex - it just +# happens to read like a glob. +# +# The match is against the NUnit *full* test name, which includes fixture arguments. That covers both +# the dedicated unified fixtures (UnifiedNetworkTransformTest) and any shared fixture parameterized +# with HostOrServer.UnifiedHost / UnifiedServer, e.g. "NetworkTransformTests(UnifiedHost,...)". +unified_test_filter: ".*Unified.*" # Scripting backends used by Standalone RunTimeTests--------------------------------------------------- From 93a7d715770fbeaabce1b0bab4e063ec5d0c6d90 Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Sun, 16 Aug 2026 16:45:36 -0500 Subject: [PATCH 6/9] update One more time... just running the UnifiedNetworkTransformTest for this phase. A more integrated one will be coming in the next PR for this effort. --- .yamato/project.metafile | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/.yamato/project.metafile b/.yamato/project.metafile index eb6c88d70b..1117c4badf 100644 --- a/.yamato/project.metafile +++ b/.yamato/project.metafile @@ -227,10 +227,16 @@ unified_test_platforms: # The other jobs' "Unity.Netcode.RuntimeTests.*" works because it is also a valid regex - it just # happens to read like a glob. # -# The match is against the NUnit *full* test name, which includes fixture arguments. That covers both -# the dedicated unified fixtures (UnifiedNetworkTransformTest) and any shared fixture parameterized -# with HostOrServer.UnifiedHost / UnifiedServer, e.g. "NetworkTransformTests(UnifiedHost,...)". -unified_test_filter: ".*Unified.*" +# The match is against the NUnit *full* test name, which includes both fixture and method arguments. +# +# DO NOT widen this to ".*Unified.*". "Unified" appearing in a test name does NOT mean that test was +# deliberately converted to run against hybrid prefabs. HostOrServer gained UnifiedServer/UnifiedHost +# members under UNIFIED_NETCODE, and NUnit expands a bare [Values] on an enum parameter (and +# enum-typed fixture constructors) to every member. So with N4E installed, much of the existing suite +# silently grows unified cases - e.g. NetworkVariableTests(Default).AllNetworkVariableTypes(UnifiedHost), +# which nobody wrote. ".*Unified.*" selected 138 tests, 84 of which failed, against the 1 test this +# job exists to validate. Deciding which of those should pass, and how they opt in, is Goal-2. +unified_test_filter: ".*UnifiedNetworkTransformTest.*" # Scripting backends used by Standalone RunTimeTests--------------------------------------------------- From aec318dd75cc02f35e4804124570046ba223a006 Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Wed, 19 Aug 2026 14:25:33 -0500 Subject: [PATCH 7/9] Apply suggestions from code review Co-authored-by: Emma --- .yamato/_triggers.yml | 6 ++---- .../Transports/Unified/UnifiedNetcodeTransport.cs | 10 ---------- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/.yamato/_triggers.yml b/.yamato/_triggers.yml index 5d8ca30e88..cb0f3a359a 100644 --- a/.yamato/_triggers.yml +++ b/.yamato/_triggers.yml @@ -128,10 +128,8 @@ pr_code_changes_checks: # Unified (NGO + N4E) validation, on demand. -# The unified tests run automatically as part of pr_code_changes_checks and develop_nightly (same as -# the CMB service tests do). This job only exists so they can also be kicked off by commenting -# "/ci unified", for PRs that pr_code_changes_checks does not cover (drafts, doc-only changes, or -# branches targeting something other than develop / develop-3.x.x / release). +# This job allows the Unified tests to be kicked off by commenting "/ci unified". +# This is useful for PRs where pr_code_changes_checks doesn't trigger. unified_pr_checks: name: Unified (NGO + N4E) checks [on demand] dependencies: diff --git a/com.unity.netcode.gameobjects/Runtime/Transports/Unified/UnifiedNetcodeTransport.cs b/com.unity.netcode.gameobjects/Runtime/Transports/Unified/UnifiedNetcodeTransport.cs index 785c959da9..6cb855e2db 100644 --- a/com.unity.netcode.gameobjects/Runtime/Transports/Unified/UnifiedNetcodeTransport.cs +++ b/com.unity.netcode.gameobjects/Runtime/Transports/Unified/UnifiedNetcodeTransport.cs @@ -124,13 +124,6 @@ public void OnUpdate(ref SystemState state) [UpdateBefore(typeof(RpcSystem))] internal partial class UnifiedNetcodeUpdateSystem : SystemBase { - // Note: this must be the parameterless ComponentSystemBase.OnCreate override, not the - // ISystem-style OnCreate(ref SystemState). With the ISystem signature this is just a new - // method that Entities never calls, so neither RequireForUpdate takes effect and OnUpdate - // runs from the very first world tick - which is before StartClient/StartServer have - // assigned Transport and NetworkManager below, and therefore a NullReferenceException. - // CreateSingleWorldHost calls AppendWorldToCurrentPlayerLoop, so the world can tick in the - // window between world creation and the transport being started. protected override void OnCreate() { RequireForUpdate(); @@ -163,9 +156,6 @@ public void SendRpc(TransportRpc rpc, Entity connectionEntity) protected override void OnUpdate() { - // Belt and braces alongside the RequireForUpdate gating in OnCreate: these are only - // assigned once the transport is started but the world can already be in the player loop - // before that happens. Exit early under this scenario. if (NetworkManager == null || Transport == null) { return; From 50a677212f0ef207e1f323afae9750014fc2b084 Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Thu, 20 Aug 2026 06:31:33 -0500 Subject: [PATCH 8/9] chore: gate unified tests in C# via UNIFIED_TESTS instead of a YAML test filter Unified selection now lives in C# instead: - unified-tests.yml sets UNIFIED_TESTS=true and passes no --testfilter; unified_test_filter is removed from project.metafile. - NetcodeIntegrationTest.UseUnifiedTests() (virtual, defaults to false) is the opt-in a fixture overrides to run in a unified pass. USE_CMB_SERVICE still takes precedence. - Adds IgnoreIfUnifiedTestsEnvironmentVariableSetAttribute and the matching NetcodeIntegrationTestHelpers.IgnoreIfUnifiedTestsEnvironmentVariableSet() so fixtures that do not apply under unified netcode opt out explicitly. - Applies that opt-out across the package and testproject fixtures that do not need to run against hybrid prefabs. --- .yamato/project.metafile | 19 --- .yamato/unified-tests.yml | 51 ++----- .../Runtime/Core/NetworkManager.cs | 4 +- .../Connection/ClientOnlyConnectionTests.cs | 2 + .../Runtime/NestedNetworkManagerTests.cs | 2 + ...NetworkManagerCustomMessageManagerTests.cs | 2 + .../Runtime/NetworkManagerEventsTests.cs | 2 + .../NetworkManagerSceneManagerTests.cs | 2 + .../Runtime/NetworkManagerTransportTests.cs | 2 + .../NetworkTransformStateTests.cs | 4 + .../UnifiedNetworkTransformTest.cs | 6 + .../Tests/Runtime/NetworkUpdateLoopTests.cs | 2 + .../NetworkVariable/NetworkVariableTests.cs | 9 ++ .../Prefabs/NetworkPrefabHandlerTests.cs | 2 + .../Tests/Runtime/Rpc/RpcQueueTests.cs | 2 + .../NetworkObjectReferenceTests.cs | 2 + .../Tests/Runtime/StartStopTests.cs | 2 + ...iedTestsEnvironmentVariableSetAttribute.cs | 26 ++++ ...stsEnvironmentVariableSetAttribute.cs.meta | 3 + .../TestHelpers/NetcodeIntegrationTest.cs | 124 +++++++++++++++++- .../NetcodeIntegrationTestHelpers.cs | 46 +++++++ .../Runtime/Timing/NetworkTimeSystemTests.cs | 2 + .../Runtime/Timing/TimeInitializationTest.cs | 2 + .../UnityTransportConnectionTests.cs | 2 + .../Runtime/Transports/UnityTransportTests.cs | 2 + testproject/.gitignore | 1 + .../Assets/Tests/Runtime/HelpUrlTests.cs | 11 ++ .../Assets/Tests/Runtime/MessageOrdering.cs | 2 + .../Runtime/MultiClientConnectionApproval.cs | 2 + .../NetworkSceneManagerDDOLTests.cs | 2 + .../SceneEventDataTests.cs | 2 + ...ariableInitializationOnNetworkSpawnTest.cs | 2 + ...oMemoryLeakOnNetworkManagerShutdownTest.cs | 2 + .../NetworkObjectParentingTests.cs | 2 + 34 files changed, 285 insertions(+), 63 deletions(-) create mode 100644 com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NUnitExtensions/IgnoreIfUnifiedTestsEnvironmentVariableSetAttribute.cs create mode 100644 com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NUnitExtensions/IgnoreIfUnifiedTestsEnvironmentVariableSetAttribute.cs.meta diff --git a/.yamato/project.metafile b/.yamato/project.metafile index 1117c4badf..3dcd7ce879 100644 --- a/.yamato/project.metafile +++ b/.yamato/project.metafile @@ -219,25 +219,6 @@ unified_test_platforms: image: package-ci/ubuntu-22.04:v4.87.0 flavor: b1.large -# Restricts the unified job to unified-only tests so nothing else runs on the alpha editor. -# -# This is a REGULAR EXPRESSION, not a glob. UTR passes it to the editor as -testFilter, which ends up -# in UnityEngine.TestRunner's FullNameFilter -> NUnit ValueMatchFilter -> new Regex(pattern).IsMatch(). -# A glob-style "*Unified*" throws "Quantifier {x,y} following nothing" and fails the whole run. -# The other jobs' "Unity.Netcode.RuntimeTests.*" works because it is also a valid regex - it just -# happens to read like a glob. -# -# The match is against the NUnit *full* test name, which includes both fixture and method arguments. -# -# DO NOT widen this to ".*Unified.*". "Unified" appearing in a test name does NOT mean that test was -# deliberately converted to run against hybrid prefabs. HostOrServer gained UnifiedServer/UnifiedHost -# members under UNIFIED_NETCODE, and NUnit expands a bare [Values] on an enum parameter (and -# enum-typed fixture constructors) to every member. So with N4E installed, much of the existing suite -# silently grows unified cases - e.g. NetworkVariableTests(Default).AllNetworkVariableTypes(UnifiedHost), -# which nobody wrote. ".*Unified.*" selected 138 tests, 84 of which failed, against the 1 test this -# job exists to validate. Deciding which of those should pass, and how they opt in, is Goal-2. -unified_test_filter: ".*UnifiedNetworkTransformTest.*" - # Scripting backends used by Standalone RunTimeTests--------------------------------------------------- diff --git a/.yamato/unified-tests.yml b/.yamato/unified-tests.yml index 9e4e6b315e..fa6dc9b308 100644 --- a/.yamato/unified-tests.yml +++ b/.yamato/unified-tests.yml @@ -2,47 +2,17 @@ --- # DESCRIPTION-------------------------------------------------------------------------- - # This job validates NGO running against Netcode for Entities (N4E) through the unified API. - # A "hybrid prefab" is an NGO prefab (NetworkObject at the root) that also carries a GhostObject. - # When one or more hybrid prefabs are in the NetworkManager's prefab list, NGO hands transform - # synchronization to N4E's snapshot system and tunnels its own batched messages over - # UnifiedNetcodeTransport (N4E's out-of-band RPC) instead of using a NetworkTransport such as UTP. - # UnifiedNetworkTransformTest is the validation test that this path works end to end. - -# WHY THIS JOB IS SEPARATE FROM EVERY OTHER JOB------------------------------------------ - # 1. It needs an editor that bundles a com.unity.netcode with the unified API. That editor - # (unified_editors.default in project.metafile) is not one of the validation_editors, and NGO - # still has to keep building and testing against editors that have no unified API at all. - # 2. It needs com.unity.netcode in the testproject so that the UNIFIED_NETCODE define is set - # (see the versionDefines in Unity.Netcode.Runtime.asmdef). The committed - # testproject/Packages/manifest.json deliberately does NOT reference it, so this job swaps in - # testproject/Packages/manifest-unified.json instead. - # 3. It is wired into pr_code_changes_checks and develop_nightly the same way the CMB service tests - # are, so it runs automatically on PRs targeting develop / develop-3.x.x / release. Be aware that - # this makes an unsupported alpha editor part of the PR gate: when N4E lands breaking changes in - # trunk this job goes red and the pin has to be bumped to unblock PRs. - # _triggers.yml also has unified_pr_checks, so it can be kicked off with "/ci unified" on PRs - # that pr_code_changes_checks does not cover. + # The NGO job that runs unified netcode hybrid prefab based integration tests. # CONFIGURATION STRUCTURE-------------------------------------------------------------- # Jobs are generated using nested loops: # 1. For all unified test platforms (currently Ubuntu only, see project.metafile) # 2. For all unified editors (currently a single pinned alpha, see project.metafile) -# TECHNICAL CONSIDERATIONS--------------------------------------------------------------- - # This job runs in Editor context only (no player builds required), like project-tests.yml. - # Only playmode is run: every unified test is an integration test and there are no unified EditMode tests. - # The run is restricted to unified tests via unified_test_filter so that nothing else is exercised - # on the alpha editor. Non-unified tests are covered by the regular jobs on the supported editors. - # packages-lock.json is removed because the committed lock was resolved against a much older editor - # and its builtin package versions do not exist in the unified editor. - # QUALITY CONSIDERATIONS-------------------------------------------------------------------- # TODO: the manifest swap means testproject/Packages/manifest-unified.json has to be kept in sync # with manifest.json by hand. Revisit once N4E is a hard dependency of NGO and one manifest # can cover both cases. - # TODO: unified_test_filter is a test-name pattern. Replacing it with an NUnit category (for - # example [Category("Unified")]) would be less fragile once more fixtures gain unified variants. #------------------------------------------------------------------------------------ @@ -58,21 +28,28 @@ unified_test_{{ project.name }}_{{ platform.name }}_{{ editor }}: {% if platform.model %} model: {{ platform.model }} # This is set only in platforms where we want non-default model to use (more information in project.metafile) {% endif %} + + variables: + # When UNIFIED_TESTS is set, only integration tests that opt in via overriding NetcodeIntegrationTest.UseUnifiedTests and returning true will run during this job. + # NetcodeIntegrationTest.UseUnifiedTests defaults to returning false (for now) making this an opt-in mode. + # Note: If USE_CMB_SERVICE is set along with UNIFIED_TESTS, then UNIFIED_TESTS is ignored until distributed authority is supported. + UNIFIED_TESTS: "true" + commands: - # Swap in the manifest that pulls in com.unity.netcode (N4E). This is what causes UNIFIED_NETCODE - # to be defined and therefore what makes the unified tests compile at all. + # Swap in the manifest that has the unified packages and versions required. + # Without the N4E package, UNIFIED_NETCODE is undefined and the unified API excluded. - cp {{ project.path }}/Packages/manifest-unified.json {{ project.path }}/Packages/manifest.json - # The committed lock was resolved against a supported editor, not the unified alpha. + # Remove the committed lock as it is configured for 6000.6 which does not contain the unified API required. - rm -f {{ project.path }}/Packages/packages-lock.json - - unity-downloader-cli --fast --wait -u {{ editor }} -c Editor # Installing basic editor for tests execution - - UnifiedTestRunner --testproject={{ project.path }} --suite=playmode --artifacts-path=test-results --editor-location=.Editor --testfilter="{{ unified_test_filter }}" --rerun-strategy=Test --retry={{ num_test_retries }} --clean-library-on-rerun --timeout={{ test_timeout }} + - unity-downloader-cli --fast --wait -u {{ editor }} -c Editor + - UnifiedTestRunner --testproject={{ project.path }} --suite=playmode --artifacts-path=test-results --editor-location=.Editor --rerun-strategy=Test --retry={{ num_test_retries }} --clean-library-on-rerun --timeout={{ test_timeout }} artifacts: logs: paths: - "test-results/**/*" dependencies: - - .yamato/_run-all.yml#run_quick_checks # initial checks to perform fast validation of common errors + - .yamato/_run-all.yml#run_quick_checks {% endfor -%} {% endfor -%} {% endfor -%} diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs index 1b702e76fc..0a3fee02d7 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs @@ -1412,12 +1412,12 @@ private bool UnifiedIsConfiguredCorrectly() { if (NetCodeConfig.Global == null) { - Log.Error(new Context(LogLevel.Error, "You must create a {nameof(NetCodeConfig)} and set it to a single world in order to run in hybrid mode!").AddTag("Unified")); + Log.Error(new Context(LogLevel.Error, $"You must create a {nameof(NetCodeConfig)} and set it to a single world in order to run in hybrid mode!").AddTag("Unified")); return false; } if (NetCodeConfig.Global.HostWorldModeSelection != NetCodeConfig.HostWorldMode.SingleWorld) { - Log.Error(new Context(LogLevel.Error, "You must configure {nameof(NetCodeConfig)} to only use a single world in order to run in hybrid mode!").AddTag("Unified")); + Log.Error(new Context(LogLevel.Error, $"You must configure {nameof(NetCodeConfig)} to only use a single world in order to run in hybrid mode!").AddTag("Unified")); return false; } return true; diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Connection/ClientOnlyConnectionTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Connection/ClientOnlyConnectionTests.cs index 549f0d8d23..fc580a721a 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Connection/ClientOnlyConnectionTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Connection/ClientOnlyConnectionTests.cs @@ -20,6 +20,8 @@ public void OneTimeSetup() { // TODO: [CmbServiceTests] if this test is deemed needed to test against the CMB server then update this test. NetcodeIntegrationTestHelpers.IgnoreIfServiceEnviromentVariableSet(); + // Excluding from unified tests. If deemed needed, update test, then remove. + NetcodeIntegrationTestHelpers.IgnoreIfUnifiedTestsEnvironmentVariableSet(); } [SetUp] diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NestedNetworkManagerTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NestedNetworkManagerTests.cs index 9f2d76dd7b..c97b94b646 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NestedNetworkManagerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NestedNetworkManagerTests.cs @@ -15,6 +15,8 @@ public void OneTimeSetup() { // TODO: [CmbServiceTests] if this test is deemed needed to test against the CMB server then update this test. NetcodeIntegrationTestHelpers.IgnoreIfServiceEnviromentVariableSet(); + // Excluding from unified tests. If deemed needed, update test, then remove. + NetcodeIntegrationTestHelpers.IgnoreIfUnifiedTestsEnvironmentVariableSet(); } [Test] diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkManagerCustomMessageManagerTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkManagerCustomMessageManagerTests.cs index da5e0b17ce..b59add95e0 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkManagerCustomMessageManagerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkManagerCustomMessageManagerTests.cs @@ -11,6 +11,8 @@ public void OneTimeSetup() { // This test does not need to run against the Rust server. NetcodeIntegrationTestHelpers.IgnoreIfServiceEnviromentVariableSet(); + // Excluding from unified tests. If deemed needed, update test, then remove. + NetcodeIntegrationTestHelpers.IgnoreIfUnifiedTestsEnvironmentVariableSet(); } [Test] diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkManagerEventsTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkManagerEventsTests.cs index 93d13c9e18..32d998c0b7 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkManagerEventsTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkManagerEventsTests.cs @@ -22,6 +22,8 @@ public void OneTimeSetup() { // TODO: [CmbServiceTests] if this test is deemed needed to test against the CMB server then update this test. NetcodeIntegrationTestHelpers.IgnoreIfServiceEnviromentVariableSet(); + // Excluding from unified tests. If deemed needed, update test, then remove. + NetcodeIntegrationTestHelpers.IgnoreIfUnifiedTestsEnvironmentVariableSet(); } /// diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkManagerSceneManagerTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkManagerSceneManagerTests.cs index 39b1bc9e14..6e0649ef69 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkManagerSceneManagerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkManagerSceneManagerTests.cs @@ -11,6 +11,8 @@ public void OneTimeSetup() { // This test does not need to run against the Rust server. NetcodeIntegrationTestHelpers.IgnoreIfServiceEnviromentVariableSet(); + // Excluding from unified tests. If deemed needed, update test, then remove. + NetcodeIntegrationTestHelpers.IgnoreIfUnifiedTestsEnvironmentVariableSet(); } [Test] diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkManagerTransportTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkManagerTransportTests.cs index 1c4d346a61..ea6f1bf210 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkManagerTransportTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkManagerTransportTests.cs @@ -16,6 +16,8 @@ public void OneTimeSetup() { // This test does not need to run against the Rust server. NetcodeIntegrationTestHelpers.IgnoreIfServiceEnviromentVariableSet(); + // Excluding from unified tests. If deemed needed, update test, then remove. + NetcodeIntegrationTestHelpers.IgnoreIfUnifiedTestsEnvironmentVariableSet(); } [Test] diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformStateTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformStateTests.cs index afa575c7ed..79c2278b10 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformStateTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/NetworkTransformStateTests.cs @@ -12,6 +12,8 @@ namespace Unity.Netcode.RuntimeTests { // These tests do not need to run against the Rust server. [IgnoreIfServiceEnvironmentVariableSet] + // These tests do not exercise hybrid prefabs. + [IgnoreIfUnifiedTestsEnvironmentVariableSet] internal class NetworkTransformStateTests { [Test] @@ -199,6 +201,8 @@ private void AssertTransformStateEquals(bool[] expected, NetworkTransformState a // These tests do not need to run against the Rust server. [IgnoreIfServiceEnvironmentVariableSet] + // These tests do not exercise hybrid prefabs. + [IgnoreIfUnifiedTestsEnvironmentVariableSet] [TestFixture(TransformSpace.World, Precision.Full, Rotation.Euler)] [TestFixture(TransformSpace.World, Precision.Half, Rotation.Euler)] [TestFixture(TransformSpace.Local, Precision.Full, Rotation.Euler)] diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/UnifiedNetworkTransformTest.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/UnifiedNetworkTransformTest.cs index fc128ae31f..a0faaa26da 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/UnifiedNetworkTransformTest.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkTransform/UnifiedNetworkTransformTest.cs @@ -40,6 +40,12 @@ public UnifiedNetworkTransformTest(HostOrServer hostOrServer) : base(hostOrServe { } + // This is the validation test for the unified API, so it always runs during a unified test pass. + protected override bool UseUnifiedTests() + { + return true; + } + protected override bool OnSetVerboseDebug() { return false; diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkUpdateLoopTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkUpdateLoopTests.cs index 7f4dba5f16..fc52e5069d 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkUpdateLoopTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkUpdateLoopTests.cs @@ -18,6 +18,8 @@ public void OneTimeSetup() { // This test does not need to run against the Rust server. NetcodeIntegrationTestHelpers.IgnoreIfServiceEnviromentVariableSet(); + // Excluding from unified tests. If deemed needed, update test, then remove. + NetcodeIntegrationTestHelpers.IgnoreIfUnifiedTestsEnvironmentVariableSet(); } [Test] diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariable/NetworkVariableTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariable/NetworkVariableTests.cs index 85a72d9f19..59fd0a9aa4 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariable/NetworkVariableTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariable/NetworkVariableTests.cs @@ -282,6 +282,11 @@ protected override void OnOneTimeTearDown() /// private void InitializeServerAndClients(HostOrServer useHost) { +#if UNIFIED_NETCODE + // HostOrServer arrives as a test method parameter here, so the base class cannot filter the + // hybrid prefab cases NUnit generates from the enum. Do it before anything is started. + ApplyUnifiedTestFilter(useHost); +#endif s_ClientNetworkVariableTestInstances.Clear(); m_PlayerPrefab.AddComponent(); @@ -351,6 +356,10 @@ private void InitializeServerAndClients(HostOrServer useHost) [Test] public void AllNetworkVariableTypes([Values] HostOrServer useHost) { +#if UNIFIED_NETCODE + // Filter before CreateNetworkObjectPrefab, which routes to CreateHybridPrefab for hybrid cases. + ApplyUnifiedTestFilter(useHost); +#endif var prefabToSpawn = CreateNetworkObjectPrefab("NetVarTest"); prefabToSpawn.AddComponent(); diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/NetworkPrefabHandlerTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/NetworkPrefabHandlerTests.cs index 56b6eb710d..bbce03bb3e 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/NetworkPrefabHandlerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/NetworkPrefabHandlerTests.cs @@ -23,6 +23,8 @@ protected override void OnOneTimeSetup() { // TODO: [CmbServiceTests] if this test is deemed needed to test against the CMB server then update this test. NetcodeIntegrationTestHelpers.IgnoreIfServiceEnviromentVariableSet(); + // Excluding from unified tests. If deemed needed, update test, then remove. + NetcodeIntegrationTestHelpers.IgnoreIfUnifiedTestsEnvironmentVariableSet(); base.OnOneTimeSetup(); } diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Rpc/RpcQueueTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Rpc/RpcQueueTests.cs index 94741ad3e5..e09c8dda4f 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Rpc/RpcQueueTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Rpc/RpcQueueTests.cs @@ -21,6 +21,8 @@ protected override void OnOneTimeSetup() { // TODO: [CmbServiceTests] if this test is deemed needed to test against the CMB server then update this test. NetcodeIntegrationTestHelpers.IgnoreIfServiceEnviromentVariableSet(); + // Excluding from unified tests. If deemed needed, update test, then remove. + NetcodeIntegrationTestHelpers.IgnoreIfUnifiedTestsEnvironmentVariableSet(); base.OnOneTimeSetup(); } diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Serialization/NetworkObjectReferenceTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Serialization/NetworkObjectReferenceTests.cs index 699f4a21f9..139a4a9e4f 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Serialization/NetworkObjectReferenceTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Serialization/NetworkObjectReferenceTests.cs @@ -28,6 +28,8 @@ protected override void OnOneTimeSetup() { // TODO: [CmbServiceTests] if this test is deemed needed to test against the CMB server then update this test. NetcodeIntegrationTestHelpers.IgnoreIfServiceEnviromentVariableSet(); + // Excluding from unified tests. If deemed needed, update test, then remove. + NetcodeIntegrationTestHelpers.IgnoreIfUnifiedTestsEnvironmentVariableSet(); base.OnOneTimeSetup(); } diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/StartStopTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/StartStopTests.cs index eaa4ebe91e..d60cca0d66 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/StartStopTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/StartStopTests.cs @@ -13,6 +13,8 @@ public void OneTimeSetup() { // This test does not need to run against the Rust server. NetcodeIntegrationTestHelpers.IgnoreIfServiceEnviromentVariableSet(); + // Excluding from unified tests. If deemed needed, update test, then remove. + NetcodeIntegrationTestHelpers.IgnoreIfUnifiedTestsEnvironmentVariableSet(); } [SetUp] diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NUnitExtensions/IgnoreIfUnifiedTestsEnvironmentVariableSetAttribute.cs b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NUnitExtensions/IgnoreIfUnifiedTestsEnvironmentVariableSetAttribute.cs new file mode 100644 index 0000000000..a30c674295 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NUnitExtensions/IgnoreIfUnifiedTestsEnvironmentVariableSetAttribute.cs @@ -0,0 +1,26 @@ +using System; +using NUnit.Framework; +using NUnit.Framework.Interfaces; +using NUnit.Framework.Internal; + +namespace Unity.Netcode.TestHelpers.Runtime +{ + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)] + internal class IgnoreIfUnifiedTestsEnvironmentVariableSetAttribute : NUnitAttribute, IApplyToTest + { + public void ApplyToTest(Test test) + { + // NotRunnable is the more weighty status, always respect it first + if (test.RunState == RunState.NotRunnable) + { + return; + } + + if (NetcodeIntegrationTestHelpers.UnifiedTestRun()) + { + test.RunState = RunState.Ignored; + test.Properties.Set("_SKIPREASON", NetcodeIntegrationTestHelpers.IgnoredForUnifiedTestsReason); + } + } + } +} diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NUnitExtensions/IgnoreIfUnifiedTestsEnvironmentVariableSetAttribute.cs.meta b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NUnitExtensions/IgnoreIfUnifiedTestsEnvironmentVariableSetAttribute.cs.meta new file mode 100644 index 0000000000..aa897ae623 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NUnitExtensions/IgnoreIfUnifiedTestsEnvironmentVariableSetAttribute.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8275a1756a05fb0581cd5b53bc4cb90d +timeCreated: 1787179374 diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs index 8cfd641c33..76c085c2dd 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs @@ -405,6 +405,107 @@ protected virtual bool UseCMBService() return m_UseCmbService; } +#if UNIFIED_NETCODE + /// + /// Indicates whether the currently running test is using hybrid prefabs via the unified (NGO + N4E) API. + /// + /// Can only be true if returns true. + protected bool m_UseUnifiedTests { get; private set; } + + private string m_UseUnifiedTestsEnvString = null; + private bool m_UseUnifiedTestsEnv; + + /// + /// Will check the environment variable once and then always return the results + /// of the first check. + /// + /// + /// This resets its properties during , so it will + /// check the environment variable once per test set. + /// + /// or + private bool GetUnifiedTestsEnvironmentVariable() + { + if (!m_UseUnifiedTestsEnv && m_UseUnifiedTestsEnvString == null) + { + m_UseUnifiedTestsEnvString = NetcodeIntegrationTestHelpers.GetUnifiedTestsEnvironmentVariable(); + if (bool.TryParse(m_UseUnifiedTestsEnvString.ToLower(), out bool isTrue)) + { + m_UseUnifiedTestsEnv = isTrue; + } + else + { + Debug.LogWarning($"The UNIFIED_TESTS ({m_UseUnifiedTestsEnvString}) value is an invalid bool string. {nameof(m_UseUnifiedTests)} is being set to false."); + m_UseUnifiedTestsEnv = false; + } + } + // A CMB service run always wins: distributed authority is not compatible with hybrid prefab spawning. + return m_UseUnifiedTestsEnv && !GetServiceEnvironmentVariable(); + } + + /// + /// Indicates whether this test's hybrid prefab cases have been validated against the unified API. + /// + /// + /// Defaults to false, which makes hybrid prefab test cases opt-in. Unified features are brought + /// online one feature set at a time, and because NUnit expands an enum parameter to every member + /// most / cases + /// exist without anyone having written them. + /// Override to return true once a test's hybrid prefab cases pass. + /// + /// if this test should run its hybrid prefab cases; otherwise it returns . + protected virtual bool UseUnifiedTests() + { + return false; + } + + /// + /// Ignores the current test case unless it is one the active test pass selects. + /// + /// Relies on throwing, so it returns only when the test should run. + private void ApplyUnifiedTestFilter() + { + // Hybrid prefab test cases exist on every test that takes a HostOrServer, because NUnit expands + // an enum parameter to all of its members. They only run during a unified test pass. + if (m_AllPrefabsAsHybrid && !GetUnifiedTestsEnvironmentVariable()) + { + Assert.Ignore(NetcodeIntegrationTestHelpers.IgnoredWithoutUnifiedTestsReason); + } + // Within a unified test pass a hybrid prefab test case still has to opt in via UseUnifiedTests. + if (m_AllPrefabsAsHybrid && !m_UseUnifiedTests) + { + Assert.Ignore(NetcodeIntegrationTestHelpers.NotOptedInForUnifiedTestsReason); + } + // Everything that is not a hybrid prefab test case is skipped during a unified test pass. Those + // tests have already run on the supported editors, and this pass only validates the unified API. + if (!m_AllPrefabsAsHybrid && GetUnifiedTestsEnvironmentVariable()) + { + Assert.Ignore(NetcodeIntegrationTestHelpers.IgnoredForUnifiedTestsReason); + } + } + + /// + /// Applies the unified test pass filtering for a test that takes its as a + /// test method parameter rather than as a fixture argument. Call it before starting any instances. + /// + /// + /// The fixture constructor never sees a test method parameter, so cannot + /// filter these cases: it runs once for the whole fixture and the value is not known yet. NUnit still + /// expands the enum to every member, so the and + /// cases are generated whether or not anyone wrote them. + /// + /// The the test method was invoked with. + protected void ApplyUnifiedTestFilter(HostOrServer hostOrServer) + { + m_AllPrefabsAsHybrid = hostOrServer == HostOrServer.UnifiedServer || hostOrServer == HostOrServer.UnifiedHost; + if (m_AllPrefabsAsHybrid && GetUnifiedTestsEnvironmentVariable()) + { + m_UseUnifiedTests = UseUnifiedTests(); + } + ApplyUnifiedTestFilter(); + } +#endif + /// /// Override this virtual method to control what kind of to use. /// @@ -591,11 +692,14 @@ public void OneTimeSetup() Assert.Ignore("[CMB-Server Test Run] Skipping non-distributed authority test."); return; } - else - { - // Otherwise, continue with the test - InternalOnOneTimeSetup(); - } +#if UNIFIED_NETCODE + // Only For Unified Tests: + // Note: this cannot filter a test that takes its HostOrServer as a test method parameter, since + // the value is not known until the method runs. Those call ApplyUnifiedTestFilter themselves. + ApplyUnifiedTestFilter(); +#endif + // Otherwise, continue with the test + InternalOnOneTimeSetup(); } private void InternalOnOneTimeSetup() @@ -1806,6 +1910,10 @@ public void OneTimeTearDown() IsRunning = false; m_UseCmbServiceEnvString = null; m_UseCmbServiceEnv = false; +#if UNIFIED_NETCODE + m_UseUnifiedTestsEnvString = null; + m_UseUnifiedTestsEnv = false; +#endif } /// @@ -2726,6 +2834,12 @@ private void InitializeTestConfiguration(NetworkTopologyTypes networkTopologyTyp #if UNIFIED_NETCODE m_UseHost = hostOrServer == HostOrServer.Host || hostOrServer == HostOrServer.DAHost || hostOrServer == HostOrServer.UnifiedHost; m_AllPrefabsAsHybrid = (hostOrServer == HostOrServer.UnifiedServer || hostOrServer == HostOrServer.UnifiedHost); + // If this is a hybrid prefab test case and the environment variable to run the unified tests + // is set, then perform the m_UseUnifiedTests check. + if (m_AllPrefabsAsHybrid && GetUnifiedTestsEnvironmentVariable()) + { + m_UseUnifiedTests = UseUnifiedTests(); + } #else m_UseHost = hostOrServer == HostOrServer.Host || hostOrServer == HostOrServer.DAHost; #endif diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTestHelpers.cs b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTestHelpers.cs index 8f32fc1916..a8103aac87 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTestHelpers.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTestHelpers.cs @@ -215,6 +215,52 @@ internal static void IgnoreIfServiceEnviromentVariableSet() } } + /// + /// Gets the UNIFIED_TESTS environment variable or returns "false" if it does not exist + /// + /// + internal static string GetUnifiedTestsEnvironmentVariable() + { +#if UNIFIED_TESTS + return "true"; +#else + return Environment.GetEnvironmentVariable("UNIFIED_TESTS") ?? "false"; +#endif + } + + /// + /// Determines whether this is a unified (NGO + N4E) hybrid prefab test run. + /// + /// + /// A CMB service run always wins: distributed authority is not compatible with hybrid prefab + /// spawning, so the two can never be the same run. + /// + /// or + internal static bool UnifiedTestRun() + { + if (bool.TryParse(GetCMBServiceEnvironentVariable(), out bool useCmbService) ? useCmbService : false) + { + return false; + } + return bool.TryParse(GetUnifiedTestsEnvironmentVariable(), out bool isTrue) ? isTrue : false; + } + + internal static readonly string IgnoredForUnifiedTestsReason = "[Unified Test Run] Skipping non-hybrid prefab test."; + internal static readonly string IgnoredWithoutUnifiedTestsReason = "[Non-Unified Test Run] Skipping hybrid prefab test."; + internal static readonly string NotOptedInForUnifiedTestsReason = "[Unified Test Run] Skipping hybrid prefab test that has not opted in via UseUnifiedTests."; + + /// + /// Use for non derived integration tests to automatically ignore the + /// test if running a unified (NGO + N4E) hybrid prefab test pass. + /// + internal static void IgnoreIfUnifiedTestsEnvironmentVariableSet() + { + if (UnifiedTestRun()) + { + Assert.Ignore(IgnoredForUnifiedTestsReason); + } + } + private static readonly string k_TransportHost = GetAddressToBind(); private static readonly ushort k_TransportPort = GetPortToBind(); diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Timing/NetworkTimeSystemTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Timing/NetworkTimeSystemTests.cs index 64cfcf030e..8dca0b69d6 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Timing/NetworkTimeSystemTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Timing/NetworkTimeSystemTests.cs @@ -22,6 +22,8 @@ public void OneTimeSetup() { // TODO: [CmbServiceTests] if this test is deemed needed to test against the CMB server then update this test. NetcodeIntegrationTestHelpers.IgnoreIfServiceEnviromentVariableSet(); + // Excluding from unified tests. If deemed needed, update test, then remove. + NetcodeIntegrationTestHelpers.IgnoreIfUnifiedTestsEnvironmentVariableSet(); } [SetUp] diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Timing/TimeInitializationTest.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Timing/TimeInitializationTest.cs index 28053fe167..cd249707a8 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Timing/TimeInitializationTest.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Timing/TimeInitializationTest.cs @@ -20,6 +20,8 @@ public void OneTimeSetup() { // TODO: [CmbServiceTests] if this test is deemed needed to test against the CMB server then update this test. NetcodeIntegrationTestHelpers.IgnoreIfServiceEnviromentVariableSet(); + // Excluding from unified tests. If deemed needed, update test, then remove. + NetcodeIntegrationTestHelpers.IgnoreIfUnifiedTestsEnvironmentVariableSet(); } [UnityTest] diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Transports/UnityTransportConnectionTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Transports/UnityTransportConnectionTests.cs index 2bdce8f205..fc83715898 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Transports/UnityTransportConnectionTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Transports/UnityTransportConnectionTests.cs @@ -28,6 +28,8 @@ public void OneTimeSetup() { // TODO: [CmbServiceTests] if this test is deemed needed to test against the CMB server then update this test. NetcodeIntegrationTestHelpers.IgnoreIfServiceEnviromentVariableSet(); + // Excluding from unified tests. If deemed needed, update test, then remove. + NetcodeIntegrationTestHelpers.IgnoreIfUnifiedTestsEnvironmentVariableSet(); } [UnityTearDown] diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Transports/UnityTransportTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Transports/UnityTransportTests.cs index 89283f83f6..9b224d6bfe 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Transports/UnityTransportTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Transports/UnityTransportTests.cs @@ -40,6 +40,8 @@ public void OneTimeSetup() { // TODO: [CmbServiceTests] if this test is deemed needed to test against the CMB server then update this test. NetcodeIntegrationTestHelpers.IgnoreIfServiceEnviromentVariableSet(); + // Excluding from unified tests. If deemed needed, update test, then remove. + NetcodeIntegrationTestHelpers.IgnoreIfUnifiedTestsEnvironmentVariableSet(); } [UnityTearDown] diff --git a/testproject/.gitignore b/testproject/.gitignore index 2800399634..adf747b1fc 100644 --- a/testproject/.gitignore +++ b/testproject/.gitignore @@ -7,6 +7,7 @@ /[Oo]bj/ /[Bb]uild/ /[Bb]uilds/ +/*[Bb]uilds/ /[Ll]ogs/ /[Uu]ser[Ss]ettings/ diff --git a/testproject/Assets/Tests/Runtime/HelpUrlTests.cs b/testproject/Assets/Tests/Runtime/HelpUrlTests.cs index 0a0eb95f54..5ad70d585d 100644 --- a/testproject/Assets/Tests/Runtime/HelpUrlTests.cs +++ b/testproject/Assets/Tests/Runtime/HelpUrlTests.cs @@ -10,6 +10,7 @@ using System.Threading.Tasks; using NUnit.Framework; using Unity.Netcode.Runtime; +using Unity.Netcode.TestHelpers.Runtime; using UnityEngine; using UnityEngine.TestTools; @@ -22,6 +23,16 @@ internal class HelpUrlTests private bool m_VerboseLogging = false; + [OneTimeSetUp] + public void OneTimeSetup() + { + // This test does not need to run against the Rust server. + NetcodeIntegrationTestHelpers.IgnoreIfServiceEnviromentVariableSet(); + + // Excluding from unified tests. If deemed needed, update test, then remove. + NetcodeIntegrationTestHelpers.IgnoreIfUnifiedTestsEnvironmentVariableSet(); + } + [UnityTest] public IEnumerator ValidateUrlsAreValid() { diff --git a/testproject/Assets/Tests/Runtime/MessageOrdering.cs b/testproject/Assets/Tests/Runtime/MessageOrdering.cs index f4ef5a7fbd..1a0af87ae2 100644 --- a/testproject/Assets/Tests/Runtime/MessageOrdering.cs +++ b/testproject/Assets/Tests/Runtime/MessageOrdering.cs @@ -24,6 +24,8 @@ public void OneTimeSetup() { // TODO: [CmbServiceTests] if this test is deemed needed to test against the CMB server then update this test. NetcodeIntegrationTestHelpers.IgnoreIfServiceEnviromentVariableSet(); + // Excluding from unified tests. If deemed needed, update test, then remove. + NetcodeIntegrationTestHelpers.IgnoreIfUnifiedTestsEnvironmentVariableSet(); } [UnitySetUp] diff --git a/testproject/Assets/Tests/Runtime/MultiClientConnectionApproval.cs b/testproject/Assets/Tests/Runtime/MultiClientConnectionApproval.cs index 61a8a8d112..344090fefb 100644 --- a/testproject/Assets/Tests/Runtime/MultiClientConnectionApproval.cs +++ b/testproject/Assets/Tests/Runtime/MultiClientConnectionApproval.cs @@ -29,6 +29,8 @@ public void OneTimeSetup() { // TODO: [CmbServiceTests] if this test is deemed needed to test against the CMB server then update this test. NetcodeIntegrationTestHelpers.IgnoreIfServiceEnviromentVariableSet(); + // Excluding from unified tests. If deemed needed, update test, then remove. + NetcodeIntegrationTestHelpers.IgnoreIfUnifiedTestsEnvironmentVariableSet(); } /// diff --git a/testproject/Assets/Tests/Runtime/NetworkSceneManager/NetworkSceneManagerDDOLTests.cs b/testproject/Assets/Tests/Runtime/NetworkSceneManager/NetworkSceneManagerDDOLTests.cs index 70ca7c24d2..bd54bba384 100644 --- a/testproject/Assets/Tests/Runtime/NetworkSceneManager/NetworkSceneManagerDDOLTests.cs +++ b/testproject/Assets/Tests/Runtime/NetworkSceneManager/NetworkSceneManagerDDOLTests.cs @@ -24,6 +24,8 @@ public void OneTimeSetup() { // This does not need to be tested against a CMB Server NetcodeIntegrationTestHelpers.IgnoreIfServiceEnviromentVariableSet(); + // Excluding from unified tests. If deemed needed, update test, then remove. + NetcodeIntegrationTestHelpers.IgnoreIfUnifiedTestsEnvironmentVariableSet(); } [UnitySetUp] diff --git a/testproject/Assets/Tests/Runtime/NetworkSceneManager/SceneEventDataTests.cs b/testproject/Assets/Tests/Runtime/NetworkSceneManager/SceneEventDataTests.cs index f1bdc67c66..1ed74831c7 100644 --- a/testproject/Assets/Tests/Runtime/NetworkSceneManager/SceneEventDataTests.cs +++ b/testproject/Assets/Tests/Runtime/NetworkSceneManager/SceneEventDataTests.cs @@ -22,6 +22,8 @@ public void OneTimeSetup() { // This test does not need to run against a CMB server NetcodeIntegrationTestHelpers.IgnoreIfServiceEnviromentVariableSet(); + // Excluding from unified tests. If deemed needed, update test, then remove. + NetcodeIntegrationTestHelpers.IgnoreIfUnifiedTestsEnvironmentVariableSet(); } /// diff --git a/testproject/Assets/Tests/Runtime/NetworkVariableInitializationOnNetworkSpawnTest.cs b/testproject/Assets/Tests/Runtime/NetworkVariableInitializationOnNetworkSpawnTest.cs index 1a54eefa90..47ac243114 100644 --- a/testproject/Assets/Tests/Runtime/NetworkVariableInitializationOnNetworkSpawnTest.cs +++ b/testproject/Assets/Tests/Runtime/NetworkVariableInitializationOnNetworkSpawnTest.cs @@ -17,6 +17,8 @@ public void OneTimeSetup() { // TODO: [CmbServiceTests] if this test is deemed needed to test against the CMB server then update this test. NetcodeIntegrationTestHelpers.IgnoreIfServiceEnviromentVariableSet(); + // Excluding from unified tests. If deemed needed, update test, then remove. + NetcodeIntegrationTestHelpers.IgnoreIfUnifiedTestsEnvironmentVariableSet(); } [UnitySetUp] diff --git a/testproject/Assets/Tests/Runtime/NoMemoryLeakOnNetworkManagerShutdownTest.cs b/testproject/Assets/Tests/Runtime/NoMemoryLeakOnNetworkManagerShutdownTest.cs index 9204454cd5..8ed88024bb 100644 --- a/testproject/Assets/Tests/Runtime/NoMemoryLeakOnNetworkManagerShutdownTest.cs +++ b/testproject/Assets/Tests/Runtime/NoMemoryLeakOnNetworkManagerShutdownTest.cs @@ -17,6 +17,8 @@ public void OneTimeSetup() { // TODO: [CmbServiceTests] if this test is deemed needed to test against the CMB server then update this test. NetcodeIntegrationTestHelpers.IgnoreIfServiceEnviromentVariableSet(); + // Excluding from unified tests. If deemed needed, update test, then remove. + NetcodeIntegrationTestHelpers.IgnoreIfUnifiedTestsEnvironmentVariableSet(); } [SetUp] diff --git a/testproject/Assets/Tests/Runtime/ObjectParenting/NetworkObjectParentingTests.cs b/testproject/Assets/Tests/Runtime/ObjectParenting/NetworkObjectParentingTests.cs index c080cac7c5..c61f8ec6e7 100644 --- a/testproject/Assets/Tests/Runtime/ObjectParenting/NetworkObjectParentingTests.cs +++ b/testproject/Assets/Tests/Runtime/ObjectParenting/NetworkObjectParentingTests.cs @@ -43,6 +43,8 @@ public void OneTimeSetup() { // TODO: [CmbServiceTests] if this test is deemed needed to test against the CMB server then update this test. NetcodeIntegrationTestHelpers.IgnoreIfServiceEnviromentVariableSet(); + // Excluding from unified tests. If deemed needed, update test, then remove. + NetcodeIntegrationTestHelpers.IgnoreIfUnifiedTestsEnvironmentVariableSet(); } private void OnSceneLoaded(Scene scene, LoadSceneMode mode) From ae1ea7eda16287dab0665eccf643cdbbfc8fa6bc Mon Sep 17 00:00:00 2001 From: Noel Stephens Date: Thu, 20 Aug 2026 08:43:55 -0500 Subject: [PATCH 9/9] Clean up comments in triggers.yml Removed comments regarding unified tests and their dependencies. --- .yamato/_triggers.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.yamato/_triggers.yml b/.yamato/_triggers.yml index cb0f3a359a..39bfb484f5 100644 --- a/.yamato/_triggers.yml +++ b/.yamato/_triggers.yml @@ -90,11 +90,6 @@ pr_code_changes_checks: # Note that our daily tests will anyway run both test configurations in "minimal supported" and "trunk" configurations - .yamato/desktop-standalone-tests.yml#desktop_standalone_test_testproject_ubuntu_il2cpp_{{ pinnedTrunk }} - .yamato/cmb-service-standalone-tests.yml#cmb_service_standalone_test_testproject_ubuntu_il2cpp_{{ pinnedTrunk }} - - # Run the unified (NGO + N4E) tests. Unlike every other job here this one runs on a pinned Unity - # alpha (unified_editors in project.metafile) rather than a supported editor, because it needs an - # editor that bundles a com.unity.netcode with the unified API. Expect it to need a pin bump - # whenever N4E lands breaking changes in trunk. See .yamato/unified-tests.yml. - .yamato/_run-all.yml#run_all_unified_tests # Run code coverage test (PRs use the pinned "safe" trunk)