diff --git a/com.unity.multiplayer.mlapi/Tests/Runtime/com.unity.multiplayer.mlapi.runtimetests.asmdef b/com.unity.multiplayer.mlapi/Tests/Runtime/com.unity.multiplayer.mlapi.runtimetests.asmdef
index da062e7cb9..6f899c67cc 100644
--- a/com.unity.multiplayer.mlapi/Tests/Runtime/com.unity.multiplayer.mlapi.runtimetests.asmdef
+++ b/com.unity.multiplayer.mlapi/Tests/Runtime/com.unity.multiplayer.mlapi.runtimetests.asmdef
@@ -8,6 +8,8 @@
"optionalUnityReferences": [
"TestAssemblies"
],
- "includePlatforms": [],
- "excludePlatforms": []
+ "defineConstraints": [
+ "UNITY_INCLUDE_TESTS",
+ "UNITY_EDITOR"
+ ]
}
\ No newline at end of file
diff --git a/testproject/Assets/Scenes.meta b/testproject/Assets/Scenes.meta
new file mode 100644
index 0000000000..3b475ec820
--- /dev/null
+++ b/testproject/Assets/Scenes.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: e6274e6e608eb41f2ace27f3a117474d
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/testproject/Assets/StreamingAssets.meta b/testproject/Assets/StreamingAssets.meta
new file mode 100644
index 0000000000..2926e11ded
--- /dev/null
+++ b/testproject/Assets/StreamingAssets.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 50c3bcef4ad84445a1f9eb2246d2e172
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/testproject/Assets/StreamingAssets/empty.txt b/testproject/Assets/StreamingAssets/empty.txt
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/testproject/Assets/StreamingAssets/empty.txt.meta b/testproject/Assets/StreamingAssets/empty.txt.meta
new file mode 100644
index 0000000000..faaff388a0
--- /dev/null
+++ b/testproject/Assets/StreamingAssets/empty.txt.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: ab2fb420f22574b13b33f6913b8736d8
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/testproject/Assets/Tests/Runtime/MultiprocessRuntime.meta b/testproject/Assets/Tests/Runtime/MultiprocessRuntime.meta
new file mode 100644
index 0000000000..6e70f368f6
--- /dev/null
+++ b/testproject/Assets/Tests/Runtime/MultiprocessRuntime.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: f415999c439ee4394bfb822a0fa30051
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/testproject/Assets/Tests/Runtime/MultiprocessRuntime/Helpers.meta b/testproject/Assets/Tests/Runtime/MultiprocessRuntime/Helpers.meta
new file mode 100644
index 0000000000..24050f230a
--- /dev/null
+++ b/testproject/Assets/Tests/Runtime/MultiprocessRuntime/Helpers.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: b9c9a4d019af9478d9fcebc1ba79d771
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/testproject/Assets/Tests/Runtime/MultiprocessRuntime/Helpers/BuildMultiprocessTestPlayer.cs b/testproject/Assets/Tests/Runtime/MultiprocessRuntime/Helpers/BuildMultiprocessTestPlayer.cs
new file mode 100644
index 0000000000..77f877a115
--- /dev/null
+++ b/testproject/Assets/Tests/Runtime/MultiprocessRuntime/Helpers/BuildMultiprocessTestPlayer.cs
@@ -0,0 +1,96 @@
+using System;
+using System.IO;
+#if UNITY_EDITOR
+using UnityEditor;
+using UnityEditor.Build.Reporting;
+#endif
+using UnityEngine;
+
+///
+/// This is needed as Unity throws "An abnormal situation has occurred: the PlayerLoop internal function has been called recursively. Please contact Customer Support with a sample project so that we can reproduce the problem and troubleshoot it."
+/// when trying to build from Setup() steps in tests.
+///
+public static class BuildMultiprocessTestPlayer
+{
+ public const string MultiprocessBaseMenuName = "MLAPI/Multiprocess Test";
+ public const string BuildAndExecuteMenuName = MultiprocessBaseMenuName + "/Build Test Player #t";
+ public const string MainSceneName = "MultiprocessTestScene";
+ private static string BuildPathDirectory => Path.Combine(Path.GetDirectoryName(Application.dataPath), "Builds","MultiprocessTests");
+ public static string BuildPath => Path.Combine(BuildPathDirectory, "MultiprocessTestPlayer");
+
+#if UNITY_EDITOR
+ [MenuItem(BuildAndExecuteMenuName)]
+ public static void BuildRelease()
+ {
+ var report = BuildPlayer();
+ if (report.summary.result != BuildResult.Succeeded)
+ {
+ throw new Exception($"Build failed! {report.summary.totalErrors} errors");
+ }
+ }
+
+ [MenuItem(MultiprocessBaseMenuName + "/Build Test Player (Debug)")]
+ public static void BuildDebug()
+ {
+ var report = BuildPlayer(true);
+ if (report.summary.result != BuildResult.Succeeded)
+ {
+ throw new Exception($"Build failed! {report.summary.totalErrors} errors");
+ }
+ }
+
+ [MenuItem(MultiprocessBaseMenuName + "/Delete Test Build")]
+ public static void DeleteBuild()
+ {
+ if (Directory.Exists(BuildPathDirectory))
+ {
+ Directory.Delete(BuildPathDirectory, recursive: true);
+ }
+ else
+ {
+ Debug.Log($"[{nameof(BuildMultiprocessTestPlayer)}] build directory does not exist ({BuildPathDirectory}) not deleting anything");
+ }
+ }
+
+ ///
+ /// Needs a separate build than the standalone test builds since we don't want the player to try to connect to the editor to do test
+ /// reporting. We only want to main node to do that, worker nodes should be dumb
+ ///
+ ///
+ private static BuildReport BuildPlayer(bool isDebug = false)
+ {
+ // Save standalone build path to file so we can read it from standalone tests (that are not running from editor)
+ File.WriteAllText(Path.Combine(Application.streamingAssetsPath, MultiprocessOrchestration.BuildInfoFileName), BuildPath);
+
+ // deleting so we don't end up testing on outdated builds if there's a build failure
+ DeleteBuild();
+
+ var buildOptions = BuildOptions.None;
+ buildOptions |= BuildOptions.IncludeTestAssemblies;
+ buildOptions |= BuildOptions.StrictMode;
+ if (isDebug)
+ {
+ buildOptions |= BuildOptions.Development;
+ buildOptions |= BuildOptions.AllowDebugging; // enable this if you want to debug your players. Your players
+ // will have more connection permission popups when launching though
+ }
+
+ var buildPathToUse = BuildPath;
+ if (Application.platform == RuntimePlatform.WindowsPlayer || Application.platform == RuntimePlatform.WindowsEditor)
+ {
+ buildPathToUse += ".exe";
+ }
+ Debug.Log($"Starting multiprocess player build using path {buildPathToUse}");
+
+ buildOptions &= ~BuildOptions.AutoRunPlayer;
+ var buildReport = BuildPipeline.BuildPlayer(
+ new[] { $"Assets/Scenes/{MainSceneName}.unity" },
+ buildPathToUse,
+ EditorUserBuildSettings.activeBuildTarget,
+ buildOptions);
+
+ Debug.Log("Build finished");
+ return buildReport;
+ }
+#endif
+}
diff --git a/testproject/Assets/Tests/Runtime/MultiprocessRuntime/Helpers/BuildMultiprocessTestPlayer.cs.meta b/testproject/Assets/Tests/Runtime/MultiprocessRuntime/Helpers/BuildMultiprocessTestPlayer.cs.meta
new file mode 100644
index 0000000000..87acf05da8
--- /dev/null
+++ b/testproject/Assets/Tests/Runtime/MultiprocessRuntime/Helpers/BuildMultiprocessTestPlayer.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: b389565fd8544431db4c24940cb569c6
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/testproject/Assets/Tests/Runtime/MultiprocessRuntime/Helpers/MultiprocessOrchestration.cs b/testproject/Assets/Tests/Runtime/MultiprocessRuntime/Helpers/MultiprocessOrchestration.cs
new file mode 100644
index 0000000000..b09ee09914
--- /dev/null
+++ b/testproject/Assets/Tests/Runtime/MultiprocessRuntime/Helpers/MultiprocessOrchestration.cs
@@ -0,0 +1,63 @@
+using System;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.IO;
+using UnityEngine;
+using Debug = UnityEngine.Debug;
+
+public class MultiprocessOrchestration
+{
+ public const string BuildInfoFileName = "buildInfo.txt";
+ public const string IsWorkerArg = "-isWorker";
+
+ public static void StartWorkerNode()
+ {
+ var workerProcess = new Process();
+
+ //TODO this should be replaced eventually by proper orchestration for all supported platforms
+ // Starting new local processes is a solution to help run perf tests locally. CI should have multi machine orchestration to
+ // run performance tests with more realistic conditions.
+ string buildInstructions = $"You probably didn't generate your build. Please make sure you build a player using the '{BuildMultiprocessTestPlayer.BuildAndExecuteMenuName}' menu";
+ try
+ {
+ var buildInfo = File.ReadAllText(Path.Combine(Application.streamingAssetsPath, BuildInfoFileName));
+ switch (Application.platform)
+ {
+ case RuntimePlatform.OSXPlayer:
+ case RuntimePlatform.OSXEditor:
+ workerProcess.StartInfo.FileName = $"{buildInfo}.app/Contents/MacOS/testproject";
+ break;
+ case RuntimePlatform.WindowsPlayer:
+ case RuntimePlatform.WindowsEditor:
+ workerProcess.StartInfo.FileName = $"{buildInfo}.exe";
+ break;
+ default:
+ throw new NotImplementedException($"{nameof(StartWorkerNode)}: Current platform is not supported");
+ }
+ }
+ catch (FileNotFoundException)
+ {
+ Debug.LogError($"Could not find build info file. {buildInstructions}");
+ throw;
+ }
+
+ workerProcess.StartInfo.UseShellExecute = false;
+ workerProcess.StartInfo.RedirectStandardError = true;
+ workerProcess.StartInfo.RedirectStandardOutput = true;
+ workerProcess.StartInfo.Arguments = $"{IsWorkerArg} -popupwindow -screen-width 100 -screen-height 100";
+ // workerNode.StartInfo.Arguments += " -deepprofiling"; // enable for deep profiling
+ try
+ {
+ var newProcessStarted = workerProcess.Start();
+ if (!newProcessStarted)
+ {
+ throw new Exception("Failed to start worker process!");
+ }
+ }
+ catch (Win32Exception e)
+ {
+ Debug.LogError($"Error starting player, {buildInstructions}, {e.Message} {e.Data} {e.ErrorCode}");
+ throw;
+ }
+ }
+}
diff --git a/testproject/Assets/Tests/Runtime/MultiprocessRuntime/Helpers/MultiprocessOrchestration.cs.meta b/testproject/Assets/Tests/Runtime/MultiprocessRuntime/Helpers/MultiprocessOrchestration.cs.meta
new file mode 100644
index 0000000000..4797a6c5a7
--- /dev/null
+++ b/testproject/Assets/Tests/Runtime/MultiprocessRuntime/Helpers/MultiprocessOrchestration.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 2b59a46cbb2c54f4d977a05103227453
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/testproject/Assets/Tests/Runtime/testproject.runtimetests.asmdef b/testproject/Assets/Tests/Runtime/testproject.runtimetests.asmdef
index 2aa7afc6f7..abe114e619 100644
--- a/testproject/Assets/Tests/Runtime/testproject.runtimetests.asmdef
+++ b/testproject/Assets/Tests/Runtime/testproject.runtimetests.asmdef
@@ -1,11 +1,15 @@
{
"name": "TestProject.RuntimeTests",
"references": [
+ "TestProject.ManualTests",
"Unity.Multiplayer.MLAPI.Runtime",
- "Unity.Multiplayer.MLAPI.RuntimeTests",
- "TestProject.ManualTests"
+ "Unity.Multiplayer.MLAPI.RuntimeTests"
],
"optionalUnityReferences": [
"TestAssemblies"
+ ],
+ "defineConstraints": [
+ "UNITY_INCLUDE_TESTS",
+ "UNITY_EDITOR"
]
}
\ No newline at end of file