From 51a1d092a0d472ebe9376136c9a802993b343023 Mon Sep 17 00:00:00 2001 From: frostebite Date: Tue, 18 Aug 2026 15:08:12 +0100 Subject: [PATCH] feat: add --docker and --local support to `game-ci test` `game-ci test` only wrapped Unity's own experimental `unity test` CLI, which requires the `unity` binary on PATH - not something GitHub-hosted runners have. This was the actual blocker on unity-test-runner's thin- wrapper migration (game-ci/cli#71): it couldn't shell out to `game-ci` the way unity-activate/unity-builder now do, since the CLI had no Docker-based test mode matching its real feature surface. Adds the classic Docker/Hub-image-driven batchmode test flow (-runTests) that unity-test-runner's action already uses today - editmode/playmode/standalone/package-mode testing, code coverage (with the coverageEnabled opt-out from unity-test-runner#311), artifact collection - as `game-ci test --docker`, reusing cli's existing Docker/license-activation infrastructure rather than reinventing it: - dist/platforms/ubuntu/steps/test.sh: ported from unity-test-runner's run_tests.sh (now living in plugins/unity/), adapted to cli's own volume-mount conventions (/UnityTestRunnerAction instead of $ACTION_FOLDER). - dist/platforms/ubuntu/steps/runsteps.sh: branches to test.sh instead of build.sh when RUN_TESTS=true - same activate/return-license steps either way. Parameterized script sourcing via STEPS_DIR (default /steps, the Docker mount point) so the same script works unmodified when run natively too (see --local below). - src/logic/unity/environment.ts: new test-specific env vars (RUN_TESTS, TEST_PLATFORMS, ARTIFACTS_PATH, COVERAGE_*, PACKAGE_*, *_REGISTRY_*) - a no-op for build/activate since they're empty there. - src/command-options/docker-test-options.ts: the new flags. Deliberately duplicates BuildOptions' docker/runtime flags rather than sharing that module - BuildOptions.configure() demands targetPlatform, which tests don't need (defaults to NoTarget's 'base' editor image). - src/model/host-runner.ts (new): --local runs the same runsteps.sh directly on the host instead of in a container - self-hosted runners with Unity already installed shouldn't need Docker at all. Mirrors MacBuilder's existing native-execution pattern (macOS never had a Docker path to begin with) and orchestrator's own local vs docker provider split. Deliberately does NOT invoke entrypoint.sh - that script does container-only setup (randomizing /etc/machine-id, useradd/groupadd for RUN_AS_HOST_USER) that would mutate a real self-hosted machine rather than a throwaway container. Explicit scope for this pass, not silently dropped: - --docker is Linux-only for now. Windows' entrypoint.ps1 doesn't know about RUN_TESTS yet (always runs build.ps1) - rather than silently running a build instead of a test, --docker now throws a clear error on non-Linux hostPlatforms before PlatformSetup.setup runs (fails fast, not after prompting for credentials). - --local is Linux-only for the same reason (Windows/macOS host-mode for test is tracked separately; macOS already runs builds natively via MacBuilder without needing --local, just not tests yet). - No GitHub Checks / HTML coverage report integration yet (results- check.ts's fancier reporting from unity-test-runner) - NUnit XML results land at --artifactsPath either way. Testing: full `bun test ./src` (156 pass, 0 fail, up from 153 - 3 new tests covering the --docker/--local/macOS-guard dispatch logic), `bun build` succeeds, and manual smoke-tested the option wiring and both new guard rails end-to-end against test-project (confirmed dispatch reaches PlatformSetup.setup / RunnerImageTag correctly, and that non-Linux hosts now fail fast with a clear message instead of silently misbehaving). --- dist/platforms/ubuntu/steps/runsteps.sh | 34 +- dist/platforms/ubuntu/steps/test.sh | 312 +++++++++ dist/test-standalone-scripts/.gitignore | 56 ++ .../Assets/Editor.meta | 8 + .../Assets/Editor/UnityTestRunnerAction.meta | 8 + .../PlayerBuildModifier.cs | 49 ++ .../PlayerBuildModifier.cs.meta | 11 + .../Assets/Player.meta | 8 + .../Assets/Player/UnityTestRunnerAction.meta | 8 + .../UnityTestRunnerAction/TestRunCallback.cs | 92 +++ .../TestRunCallback.cs.meta | 11 + .../UnityTestRunnerAction.asmdef | 15 + .../UnityTestRunnerAction.asmdef.meta | 7 + .../Packages/manifest.json | 49 ++ .../ProjectSettings/AudioManager.asset | Bin 0 -> 4148 bytes .../ProjectSettings/ClusterInputManager.asset | Bin 0 -> 4104 bytes .../ProjectSettings/DynamicsManager.asset | Bin 0 -> 4356 bytes .../ProjectSettings/EditorBuildSettings.asset | Bin 0 -> 4108 bytes .../ProjectSettings/EditorSettings.asset | Bin 0 -> 4228 bytes .../ProjectSettings/GraphicsSettings.asset | Bin 0 -> 5346 bytes .../ProjectSettings/InputManager.asset | Bin 0 -> 5520 bytes .../ProjectSettings/NavMeshAreas.asset | Bin 0 -> 4464 bytes .../ProjectSettings/Physics2DSettings.asset | Bin 0 -> 4448 bytes .../ProjectSettings/PresetManager.asset | Bin 0 -> 4104 bytes .../ProjectSettings/ProjectSettings.asset | 619 ++++++++++++++++++ .../ProjectSettings/ProjectVersion.txt | 2 + .../ProjectSettings/QualitySettings.asset | Bin 0 -> 5100 bytes .../ProjectSettings/TagManager.asset | Bin 0 -> 4308 bytes .../ProjectSettings/TimeManager.asset | Bin 0 -> 4116 bytes .../UnityConnectSettings.asset | Bin 0 -> 4328 bytes .../ProjectSettings/VFXManager.asset | Bin 0 -> 4148 bytes .../ProjectSettings/XRSettings.asset | 10 + src/command-options/docker-test-options.ts | 198 ++++++ src/command/test/unity-test-command.test.ts | 61 +- src/command/test/unity-test-command.ts | 95 ++- src/logic/unity/environment.ts | 16 + src/model/host-runner.ts | 78 +++ 37 files changed, 1727 insertions(+), 20 deletions(-) create mode 100644 dist/platforms/ubuntu/steps/test.sh create mode 100644 dist/test-standalone-scripts/.gitignore create mode 100644 dist/test-standalone-scripts/Assets/Editor.meta create mode 100644 dist/test-standalone-scripts/Assets/Editor/UnityTestRunnerAction.meta create mode 100644 dist/test-standalone-scripts/Assets/Editor/UnityTestRunnerAction/PlayerBuildModifier.cs create mode 100644 dist/test-standalone-scripts/Assets/Editor/UnityTestRunnerAction/PlayerBuildModifier.cs.meta create mode 100644 dist/test-standalone-scripts/Assets/Player.meta create mode 100644 dist/test-standalone-scripts/Assets/Player/UnityTestRunnerAction.meta create mode 100644 dist/test-standalone-scripts/Assets/Player/UnityTestRunnerAction/TestRunCallback.cs create mode 100644 dist/test-standalone-scripts/Assets/Player/UnityTestRunnerAction/TestRunCallback.cs.meta create mode 100644 dist/test-standalone-scripts/Assets/Player/UnityTestRunnerAction/UnityTestRunnerAction.asmdef create mode 100644 dist/test-standalone-scripts/Assets/Player/UnityTestRunnerAction/UnityTestRunnerAction.asmdef.meta create mode 100644 dist/test-standalone-scripts/Packages/manifest.json create mode 100644 dist/test-standalone-scripts/ProjectSettings/AudioManager.asset create mode 100644 dist/test-standalone-scripts/ProjectSettings/ClusterInputManager.asset create mode 100644 dist/test-standalone-scripts/ProjectSettings/DynamicsManager.asset create mode 100644 dist/test-standalone-scripts/ProjectSettings/EditorBuildSettings.asset create mode 100644 dist/test-standalone-scripts/ProjectSettings/EditorSettings.asset create mode 100644 dist/test-standalone-scripts/ProjectSettings/GraphicsSettings.asset create mode 100644 dist/test-standalone-scripts/ProjectSettings/InputManager.asset create mode 100644 dist/test-standalone-scripts/ProjectSettings/NavMeshAreas.asset create mode 100644 dist/test-standalone-scripts/ProjectSettings/Physics2DSettings.asset create mode 100644 dist/test-standalone-scripts/ProjectSettings/PresetManager.asset create mode 100644 dist/test-standalone-scripts/ProjectSettings/ProjectSettings.asset create mode 100644 dist/test-standalone-scripts/ProjectSettings/ProjectVersion.txt create mode 100644 dist/test-standalone-scripts/ProjectSettings/QualitySettings.asset create mode 100644 dist/test-standalone-scripts/ProjectSettings/TagManager.asset create mode 100644 dist/test-standalone-scripts/ProjectSettings/TimeManager.asset create mode 100644 dist/test-standalone-scripts/ProjectSettings/UnityConnectSettings.asset create mode 100644 dist/test-standalone-scripts/ProjectSettings/VFXManager.asset create mode 100644 dist/test-standalone-scripts/ProjectSettings/XRSettings.asset create mode 100644 src/command-options/docker-test-options.ts create mode 100644 src/model/host-runner.ts diff --git a/dist/platforms/ubuntu/steps/runsteps.sh b/dist/platforms/ubuntu/steps/runsteps.sh index 53f42eeb..1885c0ba 100644 --- a/dist/platforms/ubuntu/steps/runsteps.sh +++ b/dist/platforms/ubuntu/steps/runsteps.sh @@ -3,11 +3,19 @@ # # Run steps # -source /steps/set_extra_git_configs.sh -source /steps/set_gitcredential.sh +# STEPS_DIR defaults to /steps (the Docker container mount point). Host-mode +# execution (no Docker, see src/model/host-runner.ts) runs this script +# directly against the real filesystem and overrides STEPS_DIR to point at +# the CLI's own dist/platforms/ubuntu/steps instead - everything else in +# this file is unchanged either way. +# +STEPS_DIR="${STEPS_DIR:-/steps}" + +source "$STEPS_DIR/set_extra_git_configs.sh" +source "$STEPS_DIR/set_gitcredential.sh" if [ "$SKIP_ACTIVATION" != "true" ]; then - source /steps/activate.sh + source "$STEPS_DIR/activate.sh" # If we didn't activate successfully, exit with the exit code from the activation step. if [[ $UNITY_EXIT_CODE -ne 0 ]]; then @@ -25,10 +33,20 @@ if [ "$ACTIVATE_ONLY" = "true" ]; then exit $UNITY_EXIT_CODE fi -source /steps/build.sh +# RUN_TESTS=true (used by `game-ci test --docker`, see game-ci/cli's +# UnityTestCommand) runs the classic Docker/Hub-image-driven Unity batchmode +# test flow instead of a build - same activation/license-return steps either +# way, only the middle step differs. +if [ "$RUN_TESTS" = "true" ]; then + source "$STEPS_DIR/test.sh" + STEP_EXIT_CODE=$TEST_RUNNER_EXIT_CODE +else + source "$STEPS_DIR/build.sh" + STEP_EXIT_CODE=$BUILD_EXIT_CODE +fi if [ "$SKIP_ACTIVATION" != "true" ]; then - source /steps/return_license.sh + source "$STEPS_DIR/return_license.sh" fi # @@ -41,7 +59,7 @@ rm -r "$ACTIVATE_LICENSE_PATH" # Instructions for debugging # -if [[ $BUILD_EXIT_CODE -gt 0 ]]; then +if [[ $STEP_EXIT_CODE -gt 0 ]]; then echo "" echo "###########################" echo "# Failure #" @@ -55,7 +73,7 @@ echo "" fi; # -# Exit with code from the build step. +# Exit with code from the build/test step. # -exit $BUILD_EXIT_CODE +exit $STEP_EXIT_CODE diff --git a/dist/platforms/ubuntu/steps/test.sh b/dist/platforms/ubuntu/steps/test.sh new file mode 100644 index 00000000..6dc6e300 --- /dev/null +++ b/dist/platforms/ubuntu/steps/test.sh @@ -0,0 +1,312 @@ +#!/usr/bin/env bash + +# +# Set and display project path +# + +UNITY_PROJECT_PATH="$GITHUB_WORKSPACE/$PROJECT_PATH" +echo "Using project path \"$UNITY_PROJECT_PATH\"." + +# +# Set and display the artifacts path +# + +echo "Using artifacts path \"$ARTIFACTS_PATH\" to save test results." +FULL_ARTIFACTS_PATH=$GITHUB_WORKSPACE/$ARTIFACTS_PATH + +# +# Set and display the coverage results path +# + +echo "Using coverage results path \"$COVERAGE_RESULTS_PATH\" to save test coverage results." +FULL_COVERAGE_RESULTS_PATH=$GITHUB_WORKSPACE/$COVERAGE_RESULTS_PATH + +# +# Display custom parameters +# + +echo "Using custom parameters $CUSTOM_PARAMETERS." + +# The following tests are 2019 mode (requires Unity 2019.2.11f1 or later) +# Reference: https://docs.unity3d.com/2019.3/Documentation/Manual/CommandLineArguments.html + +# +# Display the unity version +# + +echo "Using Unity version \"$UNITY_VERSION\" to test." + +# +# Setup token for private package registry. +# + +if [ -n "$PRIVATE_REGISTRY_TOKEN" ]; then + echo "Private registry token detected, creating .upmconfig.toml" + + UPM_CONFIG_TOML_PATH="$HOME/.upmconfig.toml" + echo "Creating toml at path: $UPM_CONFIG_TOML_PATH" + + touch $UPM_CONFIG_TOML_PATH + + cat > "$UPM_CONFIG_TOML_PATH" <> "$UPM_CONFIG_TOML_PATH" < /dev/null + then + echo "jq could not be found. This is required for package mode, and is likely the result of using a custom Docker image. Please use the default image or install jq to your custom image." + exit 1 + fi + + echo "" + echo "###########################" + echo "# Package Folder #" + echo "###########################" + echo "" + + ls -la "$UNITY_PROJECT_PATH" + echo "" + + echo "Creating an empty Unity project to add the package $PACKAGE_NAME to." + + TEMP_PROJECT_PATH="./TempProject" + + unity-editor \ + -batchmode \ + -createProject "$TEMP_PROJECT_PATH" \ + -quit + + # use jq to add the package to the temp project through manually modifying Packages/manifest.json + echo "Adding package to the temporary project's dependencies and testables..." + echo "" + + PACKAGE_MANIFEST_PATH="$TEMP_PROJECT_PATH/Packages/manifest.json" + if [ ! -f "$PACKAGE_MANIFEST_PATH" ]; then + echo "Packages/manifest.json was not created properly. This indicates a problem with the CLI, not with your package. Logging directories and aborting..." + + echo "" + echo "###########################" + echo "# Temp Project Folder #" + echo "###########################" + echo "" + + ls -a "$TEMP_PROJECT_PATH" + + echo "" + echo "################################" + echo "# Temp Project Packages Folder #" + echo "################################" + echo "" + + ls -a "$TEMP_PROJECT_PATH/Packages" + + exit 1 + fi + + PACKAGE_MANIFEST_JSON=$(cat "$PACKAGE_MANIFEST_PATH") + if [ -z "$SCOPED_REGISTRY_URL" ] || [ -z "$REGISTRY_SCOPES" ]; then + echo "$PACKAGE_MANIFEST_JSON" | \ + jq \ + --arg packageName "$PACKAGE_NAME" \ + --arg projectPath "$UNITY_PROJECT_PATH" \ + '.dependencies += {"com.unity.testtools.codecoverage": "1.1.1"} | .dependencies += {"\($packageName)": "file:\($projectPath)"} | . += {testables: ["\($packageName)"]}' \ + > "$PACKAGE_MANIFEST_PATH" + + else + + echo "$PACKAGE_MANIFEST_JSON" | \ + jq \ + --arg packageName "$PACKAGE_NAME" \ + --arg projectPath "$UNITY_PROJECT_PATH" \ + --arg scopedRegistryUrl "$SCOPED_REGISTRY_URL" \ + --argjson registryScopes "$(echo "[\"$REGISTRY_SCOPES\"]" | sed 's/,/","/g')" \ + '.dependencies += {"com.unity.testtools.codecoverage": "1.1.1"} | + .dependencies += {"\($packageName)": "file:\($projectPath)"} | + . += {testables: ["\($packageName)"]} | + . += {scopedRegistries: [{"name":"dependency", "url":"\($scopedRegistryUrl)", scopes: $registryScopes}] }' \ + > "$PACKAGE_MANIFEST_PATH" + fi + + UNITY_PROJECT_PATH="$TEMP_PROJECT_PATH" + +fi + +# +# Overall info +# + +echo "" +echo "###########################" +echo "# Artifacts folder #" +echo "###########################" +echo "" +echo "Creating \"$FULL_ARTIFACTS_PATH\" if it does not exist." +mkdir -p $FULL_ARTIFACTS_PATH + +echo "" +echo "###########################" +echo "# Project directory #" +echo "###########################" +echo "" +ls -alh "$UNITY_PROJECT_PATH" + +# +# coverageEnabled gates the code-coverage flags entirely (game-ci/unity-test-runner#311): +# some Unity versions/configurations crash or fail to compile with +# com.unity.testtools.codecoverage's instrumentation enabled. Default true +# to match prior behavior when unset. +# +COVERAGE_FLAGS=() +if [ -z "$COVERAGE_ENABLED" ] || [ "$COVERAGE_ENABLED" = "true" ]; then + COVERAGE_FLAGS=(-coverageResultsPath "$FULL_COVERAGE_RESULTS_PATH" -enableCodeCoverage -debugCodeOptimization -coverageOptions "$COVERAGE_OPTIONS") +else + echo "Code coverage disabled (coverageEnabled=false)." +fi + +# +# Testing for each platform +# +for platform in ${TEST_PLATFORMS//;/ }; do + if [[ "$platform" == "standalone" ]]; then + echo "" + echo "###########################" + echo "# Building Standalone #" + echo "###########################" + echo "" + + # Create directories if they do not exist + mkdir -p "$UNITY_PROJECT_PATH/Assets/Editor/" + mkdir -p "$UNITY_PROJECT_PATH/Assets/Player/" + # Copy the scripts + cp -R "${TEST_RUNNER_ACTION_DIR:-/UnityTestRunnerAction}/Assets/Editor/" "$UNITY_PROJECT_PATH/Assets/Editor/" + cp -R "${TEST_RUNNER_ACTION_DIR:-/UnityTestRunnerAction}/Assets/Player/" "$UNITY_PROJECT_PATH/Assets/Player/" + # Verify recursive paths + ls -Ralph "$UNITY_PROJECT_PATH/Assets/Editor/" + ls -Ralph "$UNITY_PROJECT_PATH/Assets/Player/" + + runTests="-runTests -testPlatform StandaloneLinux64 -builtTestRunnerPath $UNITY_PROJECT_PATH/Build/UnityTestRunner-Standalone" + else + echo "" + echo "###########################" + echo "# Testing in $platform #" + echo "###########################" + echo "" + + if [[ "$platform" != "COMBINE_RESULTS" ]]; then + runTests="-runTests -testPlatform $platform -testResults $FULL_ARTIFACTS_PATH/$platform-results.xml" + else + runTests="-quit" + fi + fi + + eval unity-editor \ + -batchmode \ + -logFile "\"$FULL_ARTIFACTS_PATH/$platform.log\"" \ + -projectPath "\"$UNITY_PROJECT_PATH\"" \ + $runTests \ + $COVERAGE_FLAGS \ + $CUSTOM_PARAMETERS + + # Catch exit code + TEST_EXIT_CODE=$? + + # Print unity log output + cat "$FULL_ARTIFACTS_PATH/$platform.log" + + if [[ $TEST_EXIT_CODE -eq 0 && "$platform" == "standalone" ]]; then + echo "" + echo "###########################" + echo "# Testing Standalone #" + echo "###########################" + echo "" + + # Code Coverage currently only supports code ran in the Editor and not in Standalone/Player. + # https://docs.unity3d.com/Packages/com.unity.testtools.codecoverage@1.2/manual/TechnicalDetails.html#how-it-works + + xvfb-run -a -e /dev/stdout "$UNITY_PROJECT_PATH/Build/UnityTestRunner-Standalone" \ + -batchmode \ + -nographics \ + -logFile "$FULL_ARTIFACTS_PATH/$platform-player.log" \ + -testResults "$FULL_ARTIFACTS_PATH/$platform-results.xml" + + # Catch exit code + TEST_EXIT_CODE=$? + + # Print player log output + cat "$FULL_ARTIFACTS_PATH/$platform-player.log" + fi + + # Display results + if [ $TEST_EXIT_CODE -eq 0 ]; then + echo "Run succeeded, no failures occurred"; + elif [ $TEST_EXIT_CODE -eq 2 ]; then + echo "Run succeeded, some tests failed"; + elif [ $TEST_EXIT_CODE -eq 3 ]; then + echo "Run failure (other failure)"; + else + echo "Unexpected exit code $TEST_EXIT_CODE"; + fi + + if [ $TEST_EXIT_CODE -ne 0 ]; then + TEST_RUNNER_EXIT_CODE=$TEST_EXIT_CODE + fi + + echo "" + echo "###########################" + echo "# $platform Results #" + echo "###########################" + echo "" + + if [[ "$platform" != "COMBINE_RESULTS" ]]; then + cat "$FULL_ARTIFACTS_PATH/$platform-results.xml" + cat "$FULL_ARTIFACTS_PATH/$platform-results.xml" | grep test-run | grep Passed + fi +done + +# TEST_RUNNER_EXIT_CODE stays 0 if every platform above passed (loop only +# ever raises it, never resets it back down). +if [ -z "$TEST_RUNNER_EXIT_CODE" ]; then + TEST_RUNNER_EXIT_CODE=0 +fi + +# +# Permissions +# + +# Make a given user owner of all artifacts +if [[ -n "$CHOWN_FILES_TO" ]]; then + chown -R "$CHOWN_FILES_TO" "$UNITY_PROJECT_PATH" + chown -R "$CHOWN_FILES_TO" "$FULL_ARTIFACTS_PATH" + if [ -d "$FULL_COVERAGE_RESULTS_PATH" ]; then + chown -R "$CHOWN_FILES_TO" "$FULL_COVERAGE_RESULTS_PATH" + fi +fi + +# Add read permissions for everyone to all artifacts +chmod -R a+r "$UNITY_PROJECT_PATH" +chmod -R a+r "$FULL_ARTIFACTS_PATH" + +# Check if coverage results directory exists +if [ -d "$FULL_COVERAGE_RESULTS_PATH" ]; then + chmod -R a+r "$FULL_COVERAGE_RESULTS_PATH" +elif [ -z "$COVERAGE_ENABLED" ] || [ "$COVERAGE_ENABLED" = "true" ]; then + echo "Coverage results directory does not exist. If you are expecting coverage results, please make sure the Code Coverage package is installed in your unity project and that it is set up correctly." +fi diff --git a/dist/test-standalone-scripts/.gitignore b/dist/test-standalone-scripts/.gitignore new file mode 100644 index 00000000..a61293ce --- /dev/null +++ b/dist/test-standalone-scripts/.gitignore @@ -0,0 +1,56 @@ +# +# Note: Non default ignore file, as this only tests Builder script. +# + +[Ll]ibrary/ +[Tt]emp/ +[Oo]bj/ +[Bb]uild/ +[Bb]uilds/ +[Ll]ogs/ + +# Additional ignores +[Bb]in/ + +# Uncomment this line if you wish to ignore the asset store tools plugin +# [Aa]ssets/AssetStoreTools* + +# IDEs +.vs/ +.idea/ + +# Gradle cache directory +.gradle/ + +# Autogenerated VS/MD/Consulo solution and project files +ExportedObj/ +.consulo/ +#*.csproj +*.unityproj +#*.sln +*.suo +*.tmp +*.user +*.userprefs +*.pidb +*.booproj +*.svd +*.pdb +*.mdb +*.opendb +*.VC.db + +# Unity3D generated meta files +*.pidb.meta +*.pdb.meta +*.mdb.meta + +# Unity3D generated file on crash reports +sysinfo.txt + +# Builds +*.apk +*.unitypackage + +# Crashlytics generated file +crashlytics-build.properties diff --git a/dist/test-standalone-scripts/Assets/Editor.meta b/dist/test-standalone-scripts/Assets/Editor.meta new file mode 100644 index 00000000..53d7b55b --- /dev/null +++ b/dist/test-standalone-scripts/Assets/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: dea28d93f6267af4f8661eb2043f749a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/dist/test-standalone-scripts/Assets/Editor/UnityTestRunnerAction.meta b/dist/test-standalone-scripts/Assets/Editor/UnityTestRunnerAction.meta new file mode 100644 index 00000000..3754dc08 --- /dev/null +++ b/dist/test-standalone-scripts/Assets/Editor/UnityTestRunnerAction.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0cf2129f2cbdf3b4185e808b8098349d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/dist/test-standalone-scripts/Assets/Editor/UnityTestRunnerAction/PlayerBuildModifier.cs b/dist/test-standalone-scripts/Assets/Editor/UnityTestRunnerAction/PlayerBuildModifier.cs new file mode 100644 index 00000000..4381b2ba --- /dev/null +++ b/dist/test-standalone-scripts/Assets/Editor/UnityTestRunnerAction/PlayerBuildModifier.cs @@ -0,0 +1,49 @@ +using System; +using System.Linq; +using UnityEditor; +using UnityEditor.TestTools; +using UnityEngine; +using UnityEngine.TestTools; +using UnityTestRunnerAction; + +[assembly: TestPlayerBuildModifier(typeof(HeadlessPlayModeSetup))] +[assembly: PostBuildCleanup(typeof(HeadlessPlayModeSetup))] + +namespace UnityTestRunnerAction +{ + public class HeadlessPlayModeSetup : ITestPlayerBuildModifier, IPostBuildCleanup + { + private static bool s_RunningPlayerTests; + public BuildPlayerOptions ModifyOptions(BuildPlayerOptions playerOptions) + { + // Do not launch the player after the build completes. Disable the PlayerConnection. + playerOptions.options &= ~(BuildOptions.AutoRunPlayer | BuildOptions.ConnectToHost | BuildOptions.WaitForPlayerConnection); + + // Not supporting Mac currently. + playerOptions.target = SystemInfo.operatingSystemFamily == OperatingSystemFamily.Windows ? BuildTarget.StandaloneWindows64 : BuildTarget.StandaloneLinux64; + + string[] commandLineArgs = Environment.GetCommandLineArgs(); + playerOptions.locationPathName = commandLineArgs[Array.IndexOf(commandLineArgs, "-builtTestRunnerPath") + 1]; ; + + // Instruct the cleanup to exit the Editor if the run came from the command line. + // The variable is static because the cleanup is being invoked in a new instance of the class. + s_RunningPlayerTests = true; + return playerOptions; + } + + public void Cleanup() + { + if (s_RunningPlayerTests && IsRunningTestsFromCommandLine()) + { + // Exit the Editor on the next update, allowing for other PostBuildCleanup steps to run. + EditorApplication.update += () => { EditorApplication.Exit(0); }; + } + } + + private static bool IsRunningTestsFromCommandLine() + { + var commandLineArgs = Environment.GetCommandLineArgs(); + return commandLineArgs.Any(value => value == "-runTests"); + } + } +} diff --git a/dist/test-standalone-scripts/Assets/Editor/UnityTestRunnerAction/PlayerBuildModifier.cs.meta b/dist/test-standalone-scripts/Assets/Editor/UnityTestRunnerAction/PlayerBuildModifier.cs.meta new file mode 100644 index 00000000..6bd9175d --- /dev/null +++ b/dist/test-standalone-scripts/Assets/Editor/UnityTestRunnerAction/PlayerBuildModifier.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 500127a78ea2408479825b0807929249 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/dist/test-standalone-scripts/Assets/Player.meta b/dist/test-standalone-scripts/Assets/Player.meta new file mode 100644 index 00000000..c53fdb62 --- /dev/null +++ b/dist/test-standalone-scripts/Assets/Player.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: dbe67f3a46ffb8643acd08c86957d9bf +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/dist/test-standalone-scripts/Assets/Player/UnityTestRunnerAction.meta b/dist/test-standalone-scripts/Assets/Player/UnityTestRunnerAction.meta new file mode 100644 index 00000000..0365f641 --- /dev/null +++ b/dist/test-standalone-scripts/Assets/Player/UnityTestRunnerAction.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7d97b4f9ec1fb744a9aab82d6c21100d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/dist/test-standalone-scripts/Assets/Player/UnityTestRunnerAction/TestRunCallback.cs b/dist/test-standalone-scripts/Assets/Player/UnityTestRunnerAction/TestRunCallback.cs new file mode 100644 index 00000000..e8e861f8 --- /dev/null +++ b/dist/test-standalone-scripts/Assets/Player/UnityTestRunnerAction/TestRunCallback.cs @@ -0,0 +1,92 @@ +using NUnit.Framework.Interfaces; +using System; +using System.Xml; +using UnityEngine; +using UnityEngine.TestRunner; +using UnityTestRunnerAction; + +[assembly: TestRunCallback(typeof(MyTestRunCallback))] + +namespace UnityTestRunnerAction +{ + public class MyTestRunCallback : ITestRunCallback + { + private const string k_nUnitVersion = "3.5.0.0"; + + private const string k_TestRunNode = "test-run"; + private const string k_Id = "id"; + private const string k_Testcasecount = "testcasecount"; + private const string k_Result = "result"; + private const string k_Total = "total"; + private const string k_Passed = "passed"; + private const string k_Failed = "failed"; + private const string k_Inconclusive = "inconclusive"; + private const string k_Skipped = "skipped"; + private const string k_Asserts = "asserts"; + private const string k_EngineVersion = "engine-version"; + private const string k_ClrVersion = "clr-version"; + private const string k_StartTime = "start-time"; + private const string k_EndTime = "end-time"; + private const string k_Duration = "duration"; + + private const string k_TimeFormat = "u"; + + private ITest fullTest; + + public void RunStarted(ITest testsToRun) + { + if (fullTest == null) + { + fullTest = testsToRun; + } + } + + public void RunFinished(ITestResult testResults) + { + if (testResults.Test != fullTest) + { + return; + } + + string[] commandLineArgs = Environment.GetCommandLineArgs(); + string testResultsPath = commandLineArgs[Array.IndexOf(commandLineArgs, "-testResults") + 1]; + using (var writer = XmlWriter.Create(testResultsPath, new XmlWriterSettings() { Indent = true })) + { + // Manually add the outer test-run node, because testResults.ToXml doesn't include it. + + var testRunNode = new TNode(k_TestRunNode); + + testRunNode.AddAttribute(k_Id, "2"); + testRunNode.AddAttribute(k_Testcasecount, (testResults.PassCount + testResults.FailCount + testResults.SkipCount + testResults.InconclusiveCount).ToString()); + testRunNode.AddAttribute(k_Result, testResults.ResultState.ToString()); + testRunNode.AddAttribute(k_Total, (testResults.PassCount + testResults.FailCount + testResults.SkipCount + testResults.InconclusiveCount).ToString()); + testRunNode.AddAttribute(k_Passed, testResults.PassCount.ToString()); + testRunNode.AddAttribute(k_Failed, testResults.FailCount.ToString()); + testRunNode.AddAttribute(k_Inconclusive, testResults.InconclusiveCount.ToString()); + testRunNode.AddAttribute(k_Skipped, testResults.SkipCount.ToString()); + testRunNode.AddAttribute(k_Asserts, testResults.AssertCount.ToString()); + testRunNode.AddAttribute(k_EngineVersion, k_nUnitVersion); + testRunNode.AddAttribute(k_ClrVersion, Environment.Version.ToString()); + testRunNode.AddAttribute(k_StartTime, testResults.StartTime.ToString(k_TimeFormat)); + testRunNode.AddAttribute(k_EndTime, testResults.EndTime.ToString(k_TimeFormat)); + testRunNode.AddAttribute(k_Duration, testResults.Duration.ToString()); + + var resultNode = testResults.ToXml(true); + testRunNode.ChildNodes.Add(resultNode); + + testRunNode.WriteTo(writer); + writer.Flush(); + } + + Application.Quit(testResults.ResultState.Status == TestStatus.Failed ? 2 : 0); + } + + public void TestStarted(ITest test) + { + } + + public void TestFinished(ITestResult result) + { + } + } +} diff --git a/dist/test-standalone-scripts/Assets/Player/UnityTestRunnerAction/TestRunCallback.cs.meta b/dist/test-standalone-scripts/Assets/Player/UnityTestRunnerAction/TestRunCallback.cs.meta new file mode 100644 index 00000000..9c9f7771 --- /dev/null +++ b/dist/test-standalone-scripts/Assets/Player/UnityTestRunnerAction/TestRunCallback.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ee1aa3805d7b51f46a3ddefe39d76ba5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/dist/test-standalone-scripts/Assets/Player/UnityTestRunnerAction/UnityTestRunnerAction.asmdef b/dist/test-standalone-scripts/Assets/Player/UnityTestRunnerAction/UnityTestRunnerAction.asmdef new file mode 100644 index 00000000..3050849c --- /dev/null +++ b/dist/test-standalone-scripts/Assets/Player/UnityTestRunnerAction/UnityTestRunnerAction.asmdef @@ -0,0 +1,15 @@ +{ + "name": "UnityTestRunnerAction", + "references": [ + "GUID:27619889b8ba8c24980f49ee34dbb44a" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": true, + "precompiledReferences": [ + "nunit.framework.dll" + ], + "autoReferenced": false, + "defineConstraints": [] +} \ No newline at end of file diff --git a/dist/test-standalone-scripts/Assets/Player/UnityTestRunnerAction/UnityTestRunnerAction.asmdef.meta b/dist/test-standalone-scripts/Assets/Player/UnityTestRunnerAction/UnityTestRunnerAction.asmdef.meta new file mode 100644 index 00000000..4d447371 --- /dev/null +++ b/dist/test-standalone-scripts/Assets/Player/UnityTestRunnerAction/UnityTestRunnerAction.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 8a9bfe020dc3a8747afebc3a87516973 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/dist/test-standalone-scripts/Packages/manifest.json b/dist/test-standalone-scripts/Packages/manifest.json new file mode 100644 index 00000000..bae1d688 --- /dev/null +++ b/dist/test-standalone-scripts/Packages/manifest.json @@ -0,0 +1,49 @@ +{ + "dependencies": { + "com.unity.2d.sprite": "1.0.0", + "com.unity.2d.tilemap": "1.0.0", + "com.unity.ads": "2.0.8", + "com.unity.analytics": "3.3.2", + "com.unity.collab-proxy": "1.2.16", + "com.unity.ext.nunit": "1.0.0", + "com.unity.ide.rider": "1.1.0", + "com.unity.ide.vscode": "1.1.2", + "com.unity.package-manager-ui": "2.2.0", + "com.unity.purchasing": "2.0.6", + "com.unity.test-framework": "1.0.18", + "com.unity.textmeshpro": "2.0.1", + "com.unity.timeline": "1.1.0", + "com.unity.ugui": "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.screencapture": "1.0.0", + "com.unity.modules.terrain": "1.0.0", + "com.unity.modules.terrainphysics": "1.0.0", + "com.unity.modules.tilemap": "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.vehicles": "1.0.0", + "com.unity.modules.video": "1.0.0", + "com.unity.modules.vr": "1.0.0", + "com.unity.modules.wind": "1.0.0", + "com.unity.modules.xr": "1.0.0" + } +} diff --git a/dist/test-standalone-scripts/ProjectSettings/AudioManager.asset b/dist/test-standalone-scripts/ProjectSettings/AudioManager.asset new file mode 100644 index 0000000000000000000000000000000000000000..40ed38ad9a1df66d2e14babb1c49f9eb5f3bf1b2 GIT binary patch literal 4148 zcmeH~y>8S%6ov1^{6qMM27z2qP(>IbA*v)`PzqKGYq2ew^^PNl*`38}7f`uQkv9ku z1v~&v-hhXopoW?{m~*{j;$2u?fVtAlc+Tg0@A{0lFp>A2=;A67UBQZl=-%Dn;YzR) ztR?~dtg1=~h-aBp)wd6~_LmRnu(Nmb&Y|DwUxWGsV~H{10a1yRs@|0KTu*`oee|gO zB&a+Fg-h~ig86&oE<<46)SKtmE%zE=*CzkAp0Tdq0`8dS7g?vq3{d_l2gP-qQt3tVHWPO`|!#FWB<)U$uI(Y!rjkbmT~GtBi&(dqx*SMO~w{NfJMbrB)Q0@mQ}yI+1;?>fe9V0J%@lHqIw?gV%+c zdrr!Vn literal 0 HcmV?d00001 diff --git a/dist/test-standalone-scripts/ProjectSettings/ClusterInputManager.asset b/dist/test-standalone-scripts/ProjectSettings/ClusterInputManager.asset new file mode 100644 index 0000000000000000000000000000000000000000..f9df053544bfa2b34db23dd84162b1cf7fdecffc GIT binary patch literal 4104 zcmeHFyGjE=6upzpLyd23MEn7Z;EE#Y0+0GTbI6kM^U5{ zzz;G})GL->AHMnrcj4RF{pa?}GSDgpw=i}@3`(MSVd5Mog&mKcm}ey>3xxZBYq)`Y zA3Xc9K10wj*-(;EJ`fJ_9^szln3Z_)4sI1a*q>uK$gcso8Pw-U8YUYG6?>?C!ui-g zPuN(FSxGLTUm#W?V?#+kp-)4T_sh#MI#Qqbh`$9XXYEaa1J;JoCD4Q U=YVs-Ip7>{4mby#1OLr|9}pgT82|tP literal 0 HcmV?d00001 diff --git a/dist/test-standalone-scripts/ProjectSettings/DynamicsManager.asset b/dist/test-standalone-scripts/ProjectSettings/DynamicsManager.asset new file mode 100644 index 0000000000000000000000000000000000000000..b5dffeb2799a4163644d90df809a96ea3aa745b2 GIT binary patch literal 4356 zcmeH~JB%Ae7{_OG0mAV(!aF1ZLm)hJiBTkkh>-8>yIiC@Vq){?(42Q{4_WV8yX%u3 z8kZnCN(#^si3*8FMT_Xup)`mJDnv;^6Z8L%XS{o{T~pvsn(^%So9{WZwk*Vve}p)C zSP1cu5D(x(h*!@QUtcY*7SA_};?`(1@;oSCBN>gpJyz*{`T6y?u6`AKdgHI}zCQ=; z{vjdmBZgqx5GEt@Ff_B7JmHD2m&EV~PYfB1E;*c#{{g-N#l0AC2`QMFlLyT{d>_M_ z_DfXG;K!jQ=a|J$T6V`2_rz1soyTDOI}Oh!#{UiRH=&~MyD(4+=CP2kBLB;HJYvT| z?wS1G2c!Mn7=WqH=yt^ z?svd`I{v%mJg2-kj>}~1%}pw0JIGnz3Y;_k(F~cD<7X^?a0X}o+m_?93Q@{7(f$|o zUxM@gJq+Xx^ZbxkggpoOBjlw#Uo!nU4j;_#QSuC9y=3k$&d2b_ruevjeHhX1@tiG} zu0Nkx&U>5}$G@=ri9E#G@gFVcJr|`RXCvf%e|`n$*`F}lf|)tp{{HYi5TamaPL8|x z=Sf2fX6EF$dw=+@9JC+buRjr={VNdXg!%FOZdv|xj&Vs28Lr^PXYZcjQ%)G4^_>J~ zeWwg7Om^^m_F?b7AD*4VU475Z;jX^ZQ+yo%w2lA#6h9dMg*n{Ce{l|X@n4$a;G(q%&OWRB)XC8$WXRa zEIJ=tjOCtA2cjfHdRzp_JY)@pxM-cx>UiUD$}-EtVg}D zq3Z2eCG9Bifog$L-MqrODMw){8|md{Gf`>gy%q&~D!zoMGSyL-+)mzALDWF12X?&O zLy{`l(&@TvTy4sRqFtt~$GU|=soV(VZlKnD*-iDH+Kl3cg0>t)C?G`CauftQLF9@~ zQi)n-(^{&V%}^zDX)8%Cs?cT)<+dE;oS|p2?nAxSOQV|Z^a4|zQw+jJJ(gk8jN%SG zUQxY7O)@FVAlOA+Cf0PKHI=41Y$f%m)e0u*x)YtGSCu+Wb|RlU*5gR}-L_0rebA-i zy(kWRo}F)kD0Twsjed%ydTe*k;rGih_rZ4t3I@AJg=A zLfzEL-%`5OPCK$oCzCtXUkh94N?hwI<&VE!#&_Rc-NWW9$2~1R7adr1V9|j^2NoUp z|8?N!zfb%$qw3P~&FgP0-#m4J|A)=bW$rhV%K`@fPyLtTQkqi(GNfE9yTSkGMvURs F`oEAb`;-6x literal 0 HcmV?d00001 diff --git a/dist/test-standalone-scripts/ProjectSettings/EditorBuildSettings.asset b/dist/test-standalone-scripts/ProjectSettings/EditorBuildSettings.asset new file mode 100644 index 0000000000000000000000000000000000000000..789dd69ad12f475dbfc45da4f825981c4de18ce5 GIT binary patch literal 4108 zcmeHGO-~d-5Ut)1!A0al7Ev@FICvmzc0fZ+Bq0U}Bp5Y>cre85?hMP2WgswwaGPJC zKfpUbz!M1#}W~66{-c7oas_w2g^`_=kk4R}sWblrN+{21RWV-IW zs5v#~*`_1E<2Y6d$!ZB9jwc?yZ2p=&IsN(c$EWvapI66`J%ShDv{{h|C9yt4I_jWA z$w^V7uSy~^k&>Pn@o&IKkYK&$i!zkY7L)^SI`JZ*a}zJhcVOe-AV1wSB}I9R#CBR= zCKxd!rckkp%mj|?>lV}V42cn62KOY6e18wq5kq21%DMh}kyi65$UPX%{%-4B9)AZ$ z_Q&^EZr~-k09HqyQ|g18hWBfIpCid{=Ri%%@Hr%j_nRR08NQg}cmIOFPVs>oxROis zw~jpZ(Kji&eyDqXLxgg8etyH(rFzHtJt-vmkRO+|t3H{}7i5X^9_9J-^Qi*<*L+5a zlOr|G&z#SF9^lBx4l+K!X>}stt?>k79`l{Pc*-?`(7p8+h`TA3RnfK0#*U5fK|XMU=^?m I{BH_e0oTcw!~g&Q literal 0 HcmV?d00001 diff --git a/dist/test-standalone-scripts/ProjectSettings/EditorSettings.asset b/dist/test-standalone-scripts/ProjectSettings/EditorSettings.asset new file mode 100644 index 0000000000000000000000000000000000000000..0e9a94a1926933aba8c8c12a3630046bdfb6f60a GIT binary patch literal 4228 zcmeI0OK%e~5XZfg_xn}ekHQBi4Im*DgaT;_mD(y09=9TQ*G(2ScI0*1lmp5EuJ92+ z9FRC6A@L13@C6VDBqTlnSB`}Fd)K6!kbZ(4X>9NOX2$FBRN9oWtv?yt|C}*)h-R7? zyEs+2I$4-3TwX4)KfPYh^T_xbq}O}wffDcQaX#66yr}Ht~IAKs=?SOc)aJ*e0()7AW`_nY~g9v1ViJ z2eFfs1Nq$^&I9}o@yDdtcZ-d26!D&ozd$C$ZUrh~NW}3nJ%YRQrubj{V#>EWkj{uV zw0=Rln{-ENcN3k@?*E1i$iI`6N=0KL;FEN=_wM?3`QZ#r z?ic;lAddbS2k-Ws2K(y>p^f_MD0n}d*4h3#HpG+uewprn9I;&ti45+4g3KH3e-b>6 z#WcA8sUhyM4;1j36mh2!)Wwhp`eTkP-&1}D=2F5iqQ2n2Qhv6N?8@=|^oNW56C?Pg zl%E4H^_?m~Xq2uAV?tSvS@Th)V}zDGdLVQhDp^+2Xcg7kt(MY;cunY#N8wX$h|a4( zkZmrMq9Oxim{EE`tk!%TwhYoui)G%9%uUhYE1}XhpjtKhTFnP&QC7{C7ICb!d&vx*;j!tvWYB`rO&*PaH^o3)RjbFsH@E26+7@qC`cXR& z0V*z0Yt(DV>#kB}fj33m;=W*I6-9h$=+gdc4kab4<7gGhmm(41K7lXYm~8Z^Zq+(%FyLFuDPlI0;`fc|m|~|H6_=$c`azM$%|I*{ Nb=)Ebu`%?|g}-GNUPAx? literal 0 HcmV?d00001 diff --git a/dist/test-standalone-scripts/ProjectSettings/GraphicsSettings.asset b/dist/test-standalone-scripts/ProjectSettings/GraphicsSettings.asset new file mode 100644 index 0000000000000000000000000000000000000000..94ef0ba15df888b5698070089b3ff6c8675f10a3 GIT binary patch literal 5346 zcmaKw%Wovb9mlJUSqLHFnI{m;E07H=YeUFFHp}?253?RJyY_AZd6=4+vZvXe?n!q~ zYzLtTdUz+%>=Y zed<^J?O#2*cZIm+RUvNuhY;cx{Jn)=v2b+$@gws`<{#af7ysMe-wy&fzd^FU|NFnZ zdikC1>WTZ`-@j*b>$P(^yuY0l;(B6~?+Q^MWM4N5UF#|kfq4BYQM?_90^QLvm+L`( z6B_s9x)Ilm1NJw7=WiH$0d2-#bpU@HzEilUg?;=IAcFBffRpTMT#$m=6!Ig`_aii~ z#dQc*nHzl)e(JjhmtU&-IEG)yeSM$vA?&KY>$Ibu#8u&es8SWBgvzgu-oG3isy-s0Ed+<}=94^09_1)z|*j0UZ*LYdqJvyeC^s3J9y)|z1eGEVK-8YFe zMpfT858(d%@|^NZb^Sd6c%}S(tHuNIDbAli!O!#aFb6MKnF4*mJUqO}zfE_`FxKw= zw_yyPZx2q9w#cdfdBpQ^+<_Mp^Yhpw(im=ixc3L%bCvwe zl*T_+bA~ITwDZj{gK;IsfJHzgOddp1(H6=lmb{2iJWA@lULczP+6qJ-)S8+t=#(iyX9x5NL%Fp z0t@hA!*|0fy#*^%0(_D1`L73`h3hE?D*SHC&o=O*majGNW0toM;D{;Cf@Y{y@*{9FTn&GPdN{9Vh}8~7)dZ#3{fTYiDOHSNUtq5l6`ezC^Q z`n(1oo}8Z_PD8cj)-TRq&G{Ya7sn4<{)`V{lk@&9aOVHn2EJkWj~jSw`A-`7Z!G_5 z1AoKvpEd9gE#GY5f3^I%2L2z*Wdr}r@-BI6+R5$zYmt2J{~l4{^YZ(#PxOJ$&G&JcK^v+w>M1=?d$+^nM<|8?=0WNbhfV|B!r& zVILCM69w;2KYlBGR$xsQ3%EbOUoPp`IbE~j{<;i)3><$V(!M#=ra1nDQ#laY!+1LrOXDztwLXx2l}^0F%@wt!(p2@uaI>BCmFRTx^tkDD0zPBGI2A@}bp=L8 z<^yfG$n$C|QoTG(;*0^kRH=AoamPjOMEp^Wq8B(f^? zV~x@Z5FX5~D8R#Yy-$jo?Kj_6vJSgj@I)u9E~UnYdP8W zo2xN1)C2RqF7T{UqfR8l*c*(#l`vgShFz==?j67P%3dsoVeh~ogPLRAV`rBtgRYOe zc`C8Ze!=yKC!mHk++A|6ZTsZzsMJ_MYccAoezLwJ^WI=J-byl2^$5sSqL)NiHmTSa zT_MFVjMv7fkQdxf?s^ULtl5Q3ncJ_%xr#F`DVqi&Y_}xcP{WZ*Wj@B{B3lE?i<#%-8Z9aw@i24mp=2&ZerOs z-}uwE`=)g9d`Gdkwtgp~&HwwNkH0Lm1^+>UqWgOGjAsXjr+ser>^rUP;%B_JiKy1a NlTK5W1g=cL{{i>Y+Ux)T literal 0 HcmV?d00001 diff --git a/dist/test-standalone-scripts/ProjectSettings/InputManager.asset b/dist/test-standalone-scripts/ProjectSettings/InputManager.asset new file mode 100644 index 0000000000000000000000000000000000000000..7a48ab5f29de00b63593fd5855a9fe7c183a6583 GIT binary patch literal 5520 zcmeHL%Wl&^6ul;Gc|&p=5)}a?1QL)!RRN+E2=S7TkV&SkA+aOdX=zs$Y}ip2 zd;k*a2H1cQJH!W+C0jm)aLXaX=;Me- ziAuDUfoEZUFUSBV6$WQq=iHAxKIr!W4ity8LA{#)N?;W*Dh%q={8uro&##Z}up<0{ zukPZN|31dyzlPg9|Fy`k;3gGG|8)%K`J>J{ul$D?hyMo11~{om&VM7rdHz^Qy1qXD zOe3**{W@5))$lJ5^jf`t1aJmY-&251upEGb$?vHfsxz#J(I3EZTYk8VAjYJwiE#1*=Cb0 zds?_Y`Lj(yCI%D-@0`%;p218wI-*0ZtIy&5fur z&W&h^+=(>LBnXUMYVv0?sE!`om)ARsq*$QFQqP;RCZu9q6YklXsM*a{gR9jEjN5CP zRndX?Dq92NACvWXsAZ&eel0Fu0v>;!o%}~>DH;~fk2J97SN?aau2Zi+m=Yp-R$6kR z6W5ae3o)OyUK6RZ`}ob(%e-~Ff=-l=>$J5AJQ+**JQ3ta^NH3?axG$>yfN~4(LW!2 zDz{>C1$K0^pHZZlT#IR%y6^Iu$@Q3~srzPYrme>`J>4@~5C6B_Z9Q#R;d>F)3b-r$ zmTM~{%>!?D-n!4yMy-YI^M+dm`knVFf2`L`yP90Q@ZpWR$(v&tX=5!*CR_U-wuGBr literal 0 HcmV?d00001 diff --git a/dist/test-standalone-scripts/ProjectSettings/NavMeshAreas.asset b/dist/test-standalone-scripts/ProjectSettings/NavMeshAreas.asset new file mode 100644 index 0000000000000000000000000000000000000000..eaba5b14daded5d2d9d5070b147aec6db641f50e GIT binary patch literal 4464 zcmeI0OHUI~6vxlB6nrbb0Tp~MOe7W|hK;n)C>SLOB*vAwwAV5?(`h<0MqQY3FMF35 z6BoKLkq;nSok0e@$x`saprDHtEQuBw_$KpXEFfO37eo}F%@}!J8x1@Qv=Qf7`o?U4*TDK{ zaWg1OCs|n0@j1W%U1$bJOmpldo?`uO$N{?049X?;?_t>}Xb7LW6Z`K_T9V)46|q0= zz4I^LMX#8Ru#U6u;ubgL2lwOrD!w}o*^lx!!#>Wx2RuL*T6_NXLf!}&dgLdbzkORs zc^T%5?BV=lJnn}YpbHK8L%hjToIk824S4{^{d~=Pw~+E8{#^5e9r&u|T&xL;;$Lch zI6=4ef35kE4t!nnqaFAs%~7Xui|+r0Mcn^*9I_vs@4M!G+i>LXr{?_~_;1YzI`AGY zF6@6|8{Wt43OD{!&qDJ1%h4Lw|G4I-5MMwST2x;}=BTeh$U(@^x7XJY#0_+zwb$3_ z2*fy^zv%fq13l1?2hLyO`8x}(5a+o>{hm`XNgjXx&bRSd@#wa6m**PJiY$8z((}w( z)g{r8!X*uhJShoJP)*p9%8u)iegBqly<%0?yu~MVIa6Y#6|BQ1ebcJ6L=pR}^5;dx z^m!-f)6%Rid!fHzIdvJjQdVNFTPS}>pTnIk9E7fnkl-HRMiF8dAP$vI1urOhXb?yfFTl1qNIb(0Ea zeBbMq;7qmdJ7D7FZRzb^yL;H3 zgGqaUm}p|ulb#?dS2xB3#zY|=ghWq9J@|3glLxtw==%RxSMBudY%)=a&rdcUey zuU@_W7$Md?BgER{LWtXixD`J_4DKG-+dt4huxD~W{I#&KU|A^@VqszR{N-a?U%Ofw zJ$E_U|H`rLBS7=l2!XFGPY97<%7W}9sgBaZ=iEw>d~At?V`-9A7xJILZ-e3n)D;r+ zNl9Tra~8HYq0RX#sT6%uQfR&zZSvHgKD5$8`+aCWiFy<2wW!&``M(*@O(!R|98Og6?aH&A|-|9A1c0q%6vj8i_V|Vz`6fB zr8be0Li6txze|!tN(#+?Q~Yj85-BM(Ux5pQ_BTqBNJ*jjdc}F2U^`M$XuebNdn8Gu zq|kgsab8Eu&36^Q*T7FH-e=(ND}JAWf1&vO2L7YsTMYcF;#&=T18y9Q-vb6dsQ80D z+>-e*4vr%d+a&J@<{$ItxZ>M8NVYU@DgKawzp41c2L6HKj~MvpitjM+Zx!DOzV!Y@ z|9@4y-@yM=e3yZ*LvR_N-Q@WMGA;W0+y>7051=kx|0veq2grNJpyX#D)582b3tmz4 zABEZyXB1^SD;l`=Z`i=K ze|YIxvOi0Fhv)md@Q?2=CGrG9737zNoK0BC4?=I{HKnbU^7+0IHef;=BJW_y`Tp|~ z=98C~@XAy>a_iCHaMg)p*KbB*`o)8xebSBFVotP0r4onx)u1suc0$+o28Rz|)UY#Y zx4c*@p((kZQyxa6V#iaman$jGI!u)@(T?4~r};_{xomeS^~l;(=tNV2*TBFrdv4VA z9Xl)rVd&I3C)F1GX3OhU*FxKmJh@JF#&ML1BSGAy8nRo{O*@R8YTfo67+h4;+-YZg zvOGOw*JBN-s|8ihab_?ybw~DUfrmZ$by`sV^ypQHih&=)$ied z)PqZ|-#CnwoG9*b;klUxlB*WF&8CCnUgBUGr|LJVfp?NHinB?QWptQaA6}Lzqb58O zxIWxkelF~~nSCx}wiARxD-NpeH1bpB>$5FfG){ELjn(#e4Jc(bMk1Ddbub}G(e}Iv zyM7`X_Yr7&LZ;{uN2UOdFTzEzI0VDxa)`Uk{mCGlrj3F(Yqz87RDc|iM=mXA`#Pbs zIvsWx$f^s4p&|N}9!en$gIOJ4ZM`sSpFmo9K`27e6cfUh6Vjy%5ig#U(oCvdJPuCk zsJWOPI%>Ch+-lRf&xG!rytifVZ^yHp%zf|w=6@psUtZaCG3zP+zv5L7CCPpB^(bEl z3PN1MWBHR}+RyV9Ah+s=Eqv9;t6$MThyNY;dr4Kk%dt|YE7OJlaalFDP|#%+T;uJx x*5|BGlq5I*(Jr*vPj#;*$$>r)KCA!D@;87BCzt>L literal 0 HcmV?d00001 diff --git a/dist/test-standalone-scripts/ProjectSettings/PresetManager.asset b/dist/test-standalone-scripts/ProjectSettings/PresetManager.asset new file mode 100644 index 0000000000000000000000000000000000000000..ec72a0e0850e941b44aa0f0649751415dc6f1f7e GIT binary patch literal 4104 zcmeHFJxc>Y5S>j-;`g^U3jTxWi70{uiy(?0M6nO2o+g@znp3b4uJadYYa^*_6e}w` zYYSU{isyT`Ik=5K!5x^{+4p8<_uigJ`cq`EQ$$9vViB30aTfDV-kCde`zp%3q%mRY1t;l@dQ`T(2y=VTibsSk0!gUNS9?{f>B=MP{t&(9d= zM}N{Xi?qexAHUB~OeFYw~-f;IK)H1r*7<4YG_ z!}B-YQ}@Wbkb1f39lDn_f1}#)rIqWF2+`^8^_eF=L%qD^`qe9s$XXFwJ?Mb@QpxAQ zm1<2>CD*UC*Q~g;+Mc_AA_w|dJ-=a|ES$8YsRAbIto2ICw`%|Q4594CDqt0`3RnfK S0#*U5fK|XMU={fH3j6}Z(xfE- literal 0 HcmV?d00001 diff --git a/dist/test-standalone-scripts/ProjectSettings/ProjectSettings.asset b/dist/test-standalone-scripts/ProjectSettings/ProjectSettings.asset new file mode 100644 index 00000000..87709fd3 --- /dev/null +++ b/dist/test-standalone-scripts/ProjectSettings/ProjectSettings.asset @@ -0,0 +1,619 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!129 &1 +PlayerSettings: + m_ObjectHideFlags: 0 + serializedVersion: 18 + productGUID: 52397747394a6224aa209092f5947d3d + AndroidProfiler: 0 + AndroidFilterTouchesWhenObscured: 0 + AndroidEnableSustainedPerformanceMode: 0 + defaultScreenOrientation: 4 + targetDevice: 2 + useOnDemandResources: 0 + accelerometerFrequency: 60 + companyName: DefaultCompany + productName: test-standalone-scripts + defaultCursor: {fileID: 0} + cursorHotspot: {x: 0, y: 0} + m_SplashScreenBackgroundColor: {r: 0.13725491, g: 0.12156863, b: 0.1254902, a: 1} + m_ShowUnitySplashScreen: 1 + m_ShowUnitySplashLogo: 1 + m_SplashScreenOverlayOpacity: 1 + m_SplashScreenAnimation: 1 + m_SplashScreenLogoStyle: 1 + m_SplashScreenDrawMode: 0 + m_SplashScreenBackgroundAnimationZoom: 1 + m_SplashScreenLogoAnimationZoom: 1 + m_SplashScreenBackgroundLandscapeAspect: 1 + m_SplashScreenBackgroundPortraitAspect: 1 + m_SplashScreenBackgroundLandscapeUvs: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + m_SplashScreenBackgroundPortraitUvs: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + m_SplashScreenLogos: [] + m_VirtualRealitySplashScreen: {fileID: 0} + m_HolographicTrackingLossScreen: {fileID: 0} + defaultScreenWidth: 1024 + defaultScreenHeight: 768 + defaultScreenWidthWeb: 960 + defaultScreenHeightWeb: 600 + m_StereoRenderingPath: 0 + m_ActiveColorSpace: 0 + m_MTRendering: 1 + m_StackTraceTypes: 010000000100000001000000010000000100000001000000 + iosShowActivityIndicatorOnLoading: -1 + androidShowActivityIndicatorOnLoading: -1 + displayResolutionDialog: 0 + iosUseCustomAppBackgroundBehavior: 0 + iosAllowHTTPDownload: 1 + allowedAutorotateToPortrait: 1 + allowedAutorotateToPortraitUpsideDown: 1 + allowedAutorotateToLandscapeRight: 1 + allowedAutorotateToLandscapeLeft: 1 + useOSAutorotation: 1 + use32BitDisplayBuffer: 1 + preserveFramebufferAlpha: 0 + disableDepthAndStencilBuffers: 0 + androidStartInFullscreen: 1 + androidRenderOutsideSafeArea: 1 + androidUseSwappy: 0 + androidBlitType: 0 + defaultIsNativeResolution: 1 + macRetinaSupport: 1 + runInBackground: 0 + captureSingleScreen: 0 + muteOtherAudioSources: 0 + Prepare IOS For Recording: 0 + Force IOS Speakers When Recording: 0 + deferSystemGesturesMode: 0 + hideHomeButton: 0 + submitAnalytics: 1 + usePlayerLog: 1 + bakeCollisionMeshes: 0 + forceSingleInstance: 0 + useFlipModelSwapchain: 1 + resizableWindow: 0 + useMacAppStoreValidation: 0 + macAppStoreCategory: public.app-category.games + gpuSkinning: 0 + graphicsJobs: 0 + xboxPIXTextureCapture: 0 + xboxEnableAvatar: 0 + xboxEnableKinect: 0 + xboxEnableKinectAutoTracking: 0 + xboxEnableFitness: 0 + visibleInBackground: 1 + allowFullscreenSwitch: 1 + graphicsJobMode: 0 + fullscreenMode: 1 + xboxSpeechDB: 0 + xboxEnableHeadOrientation: 0 + xboxEnableGuest: 0 + xboxEnablePIXSampling: 0 + metalFramebufferOnly: 0 + xboxOneResolution: 0 + xboxOneSResolution: 0 + xboxOneXResolution: 3 + xboxOneMonoLoggingLevel: 0 + xboxOneLoggingLevel: 1 + xboxOneDisableEsram: 0 + xboxOnePresentImmediateThreshold: 0 + switchQueueCommandMemory: 1048576 + switchQueueControlMemory: 16384 + switchQueueComputeMemory: 262144 + switchNVNShaderPoolsGranularity: 33554432 + switchNVNDefaultPoolsGranularity: 16777216 + switchNVNOtherPoolsGranularity: 16777216 + vulkanEnableSetSRGBWrite: 0 + m_SupportedAspectRatios: + 4:3: 1 + 5:4: 1 + 16:10: 1 + 16:9: 1 + Others: 1 + bundleVersion: 1.0 + preloadedAssets: [] + metroInputSource: 0 + wsaTransparentSwapchain: 0 + m_HolographicPauseOnTrackingLoss: 1 + xboxOneDisableKinectGpuReservation: 1 + xboxOneEnable7thCore: 1 + vrSettings: + cardboard: + depthFormat: 0 + enableTransitionView: 0 + daydream: + depthFormat: 0 + useSustainedPerformanceMode: 0 + enableVideoLayer: 0 + useProtectedVideoMemory: 0 + minimumSupportedHeadTracking: 0 + maximumSupportedHeadTracking: 1 + hololens: + depthFormat: 1 + depthBufferSharingEnabled: 1 + lumin: + depthFormat: 0 + frameTiming: 2 + enableGLCache: 0 + glCacheMaxBlobSize: 524288 + glCacheMaxFileSize: 8388608 + oculus: + sharedDepthBuffer: 1 + dashSupport: 1 + lowOverheadMode: 0 + protectedContext: 0 + v2Signing: 0 + enable360StereoCapture: 0 + isWsaHolographicRemotingEnabled: 0 + protectGraphicsMemory: 0 + enableFrameTimingStats: 0 + useHDRDisplay: 0 + m_ColorGamuts: 00000000 + targetPixelDensity: 30 + resolutionScalingMode: 0 + androidSupportedAspectRatio: 1 + androidMaxAspectRatio: 2.1 + applicationIdentifier: {} + buildNumber: {} + AndroidBundleVersionCode: 1 + AndroidMinSdkVersion: 16 + AndroidTargetSdkVersion: 0 + AndroidPreferredInstallLocation: 1 + aotOptions: + stripEngineCode: 1 + iPhoneStrippingLevel: 0 + iPhoneScriptCallOptimization: 0 + ForceInternetPermission: 0 + ForceSDCardPermission: 0 + CreateWallpaper: 0 + APKExpansionFiles: 0 + keepLoadedShadersAlive: 0 + StripUnusedMeshComponents: 0 + VertexChannelCompressionMask: 4054 + iPhoneSdkVersion: 988 + iOSTargetOSVersionString: 9.0 + tvOSSdkVersion: 0 + tvOSRequireExtendedGameController: 0 + tvOSTargetOSVersionString: 9.0 + uIPrerenderedIcon: 0 + uIRequiresPersistentWiFi: 0 + uIRequiresFullScreen: 1 + uIStatusBarHidden: 1 + uIExitOnSuspend: 0 + uIStatusBarStyle: 0 + iPhoneSplashScreen: {fileID: 0} + iPhoneHighResSplashScreen: {fileID: 0} + iPhoneTallHighResSplashScreen: {fileID: 0} + iPhone47inSplashScreen: {fileID: 0} + iPhone55inPortraitSplashScreen: {fileID: 0} + iPhone55inLandscapeSplashScreen: {fileID: 0} + iPhone58inPortraitSplashScreen: {fileID: 0} + iPhone58inLandscapeSplashScreen: {fileID: 0} + iPadPortraitSplashScreen: {fileID: 0} + iPadHighResPortraitSplashScreen: {fileID: 0} + iPadLandscapeSplashScreen: {fileID: 0} + iPadHighResLandscapeSplashScreen: {fileID: 0} + iPhone65inPortraitSplashScreen: {fileID: 0} + iPhone65inLandscapeSplashScreen: {fileID: 0} + iPhone61inPortraitSplashScreen: {fileID: 0} + iPhone61inLandscapeSplashScreen: {fileID: 0} + appleTVSplashScreen: {fileID: 0} + appleTVSplashScreen2x: {fileID: 0} + tvOSSmallIconLayers: [] + tvOSSmallIconLayers2x: [] + tvOSLargeIconLayers: [] + tvOSLargeIconLayers2x: [] + tvOSTopShelfImageLayers: [] + tvOSTopShelfImageLayers2x: [] + tvOSTopShelfImageWideLayers: [] + tvOSTopShelfImageWideLayers2x: [] + iOSLaunchScreenType: 0 + iOSLaunchScreenPortrait: {fileID: 0} + iOSLaunchScreenLandscape: {fileID: 0} + iOSLaunchScreenBackgroundColor: + serializedVersion: 2 + rgba: 0 + iOSLaunchScreenFillPct: 100 + iOSLaunchScreenSize: 100 + iOSLaunchScreenCustomXibPath: + iOSLaunchScreeniPadType: 0 + iOSLaunchScreeniPadImage: {fileID: 0} + iOSLaunchScreeniPadBackgroundColor: + serializedVersion: 2 + rgba: 0 + iOSLaunchScreeniPadFillPct: 100 + iOSLaunchScreeniPadSize: 100 + iOSLaunchScreeniPadCustomXibPath: + iOSUseLaunchScreenStoryboard: 0 + iOSLaunchScreenCustomStoryboardPath: + iOSDeviceRequirements: [] + iOSURLSchemes: [] + iOSBackgroundModes: 0 + iOSMetalForceHardShadows: 0 + metalEditorSupport: 1 + metalAPIValidation: 1 + iOSRenderExtraFrameOnPause: 0 + appleDeveloperTeamID: + iOSManualSigningProvisioningProfileID: + tvOSManualSigningProvisioningProfileID: + iOSManualSigningProvisioningProfileType: 0 + tvOSManualSigningProvisioningProfileType: 0 + appleEnableAutomaticSigning: 0 + iOSRequireARKit: 0 + iOSAutomaticallyDetectAndAddCapabilities: 1 + appleEnableProMotion: 0 + clonedFromGUID: 00000000000000000000000000000000 + templatePackageId: + templateDefaultScene: + AndroidTargetArchitectures: 1 + AndroidSplashScreenScale: 0 + androidSplashScreen: {fileID: 0} + AndroidKeystoreName: + AndroidKeyaliasName: + AndroidBuildApkPerCpuArchitecture: 0 + AndroidTVCompatibility: 0 + AndroidIsGame: 1 + AndroidEnableTango: 0 + androidEnableBanner: 1 + androidUseLowAccuracyLocation: 0 + androidUseCustomKeystore: 0 + m_AndroidBanners: + - width: 320 + height: 180 + banner: {fileID: 0} + androidGamepadSupportLevel: 0 + AndroidValidateAppBundleSize: 1 + AndroidAppBundleSizeToValidate: 150 + resolutionDialogBanner: {fileID: 0} + m_BuildTargetIcons: [] + m_BuildTargetPlatformIcons: [] + m_BuildTargetBatching: [] + m_BuildTargetGraphicsAPIs: [] + m_BuildTargetVRSettings: [] + openGLRequireES31: 0 + openGLRequireES31AEP: 0 + openGLRequireES32: 0 + vuforiaEnabled: 0 + m_TemplateCustomTags: {} + mobileMTRendering: + Android: 1 + iPhone: 1 + tvOS: 1 + m_BuildTargetGroupLightmapEncodingQuality: [] + m_BuildTargetGroupLightmapSettings: [] + playModeTestRunnerEnabled: 0 + runPlayModeTestAsEditModeTest: 0 + actionOnDotNetUnhandledException: 1 + enableInternalProfiler: 0 + logObjCUncaughtExceptions: 1 + enableCrashReportAPI: 0 + cameraUsageDescription: + locationUsageDescription: + microphoneUsageDescription: + switchNetLibKey: + switchSocketMemoryPoolSize: 6144 + switchSocketAllocatorPoolSize: 128 + switchSocketConcurrencyLimit: 14 + switchScreenResolutionBehavior: 2 + switchUseCPUProfiler: 0 + switchApplicationID: 0x01004b9000490000 + switchNSODependencies: + switchTitleNames_0: + switchTitleNames_1: + switchTitleNames_2: + switchTitleNames_3: + switchTitleNames_4: + switchTitleNames_5: + switchTitleNames_6: + switchTitleNames_7: + switchTitleNames_8: + switchTitleNames_9: + switchTitleNames_10: + switchTitleNames_11: + switchTitleNames_12: + switchTitleNames_13: + switchTitleNames_14: + switchPublisherNames_0: + switchPublisherNames_1: + switchPublisherNames_2: + switchPublisherNames_3: + switchPublisherNames_4: + switchPublisherNames_5: + switchPublisherNames_6: + switchPublisherNames_7: + switchPublisherNames_8: + switchPublisherNames_9: + switchPublisherNames_10: + switchPublisherNames_11: + switchPublisherNames_12: + switchPublisherNames_13: + switchPublisherNames_14: + switchIcons_0: {fileID: 0} + switchIcons_1: {fileID: 0} + switchIcons_2: {fileID: 0} + switchIcons_3: {fileID: 0} + switchIcons_4: {fileID: 0} + switchIcons_5: {fileID: 0} + switchIcons_6: {fileID: 0} + switchIcons_7: {fileID: 0} + switchIcons_8: {fileID: 0} + switchIcons_9: {fileID: 0} + switchIcons_10: {fileID: 0} + switchIcons_11: {fileID: 0} + switchIcons_12: {fileID: 0} + switchIcons_13: {fileID: 0} + switchIcons_14: {fileID: 0} + switchSmallIcons_0: {fileID: 0} + switchSmallIcons_1: {fileID: 0} + switchSmallIcons_2: {fileID: 0} + switchSmallIcons_3: {fileID: 0} + switchSmallIcons_4: {fileID: 0} + switchSmallIcons_5: {fileID: 0} + switchSmallIcons_6: {fileID: 0} + switchSmallIcons_7: {fileID: 0} + switchSmallIcons_8: {fileID: 0} + switchSmallIcons_9: {fileID: 0} + switchSmallIcons_10: {fileID: 0} + switchSmallIcons_11: {fileID: 0} + switchSmallIcons_12: {fileID: 0} + switchSmallIcons_13: {fileID: 0} + switchSmallIcons_14: {fileID: 0} + switchManualHTML: + switchAccessibleURLs: + switchLegalInformation: + switchMainThreadStackSize: 1048576 + switchPresenceGroupId: + switchLogoHandling: 0 + switchReleaseVersion: 0 + switchDisplayVersion: 1.0.0 + switchStartupUserAccount: 0 + switchTouchScreenUsage: 0 + switchSupportedLanguagesMask: 0 + switchLogoType: 0 + switchApplicationErrorCodeCategory: + switchUserAccountSaveDataSize: 0 + switchUserAccountSaveDataJournalSize: 0 + switchApplicationAttribute: 0 + switchCardSpecSize: -1 + switchCardSpecClock: -1 + switchRatingsMask: 0 + switchRatingsInt_0: 0 + switchRatingsInt_1: 0 + switchRatingsInt_2: 0 + switchRatingsInt_3: 0 + switchRatingsInt_4: 0 + switchRatingsInt_5: 0 + switchRatingsInt_6: 0 + switchRatingsInt_7: 0 + switchRatingsInt_8: 0 + switchRatingsInt_9: 0 + switchRatingsInt_10: 0 + switchRatingsInt_11: 0 + switchLocalCommunicationIds_0: + switchLocalCommunicationIds_1: + switchLocalCommunicationIds_2: + switchLocalCommunicationIds_3: + switchLocalCommunicationIds_4: + switchLocalCommunicationIds_5: + switchLocalCommunicationIds_6: + switchLocalCommunicationIds_7: + switchParentalControl: 0 + switchAllowsScreenshot: 1 + switchAllowsVideoCapturing: 1 + switchAllowsRuntimeAddOnContentInstall: 0 + switchDataLossConfirmation: 0 + switchUserAccountLockEnabled: 0 + switchSystemResourceMemory: 16777216 + switchSupportedNpadStyles: 6 + switchNativeFsCacheSize: 32 + switchIsHoldTypeHorizontal: 0 + switchSupportedNpadCount: 8 + switchSocketConfigEnabled: 0 + switchTcpInitialSendBufferSize: 32 + switchTcpInitialReceiveBufferSize: 64 + switchTcpAutoSendBufferSizeMax: 256 + switchTcpAutoReceiveBufferSizeMax: 256 + switchUdpSendBufferSize: 9 + switchUdpReceiveBufferSize: 42 + switchSocketBufferEfficiency: 4 + switchSocketInitializeEnabled: 1 + switchNetworkInterfaceManagerInitializeEnabled: 1 + switchPlayerConnectionEnabled: 1 + ps4NPAgeRating: 12 + ps4NPTitleSecret: + ps4NPTrophyPackPath: + ps4ParentalLevel: 11 + ps4ContentID: ED1633-NPXX51362_00-0000000000000000 + ps4Category: 0 + ps4MasterVersion: 01.00 + ps4AppVersion: 01.00 + ps4AppType: 0 + ps4ParamSfxPath: + ps4VideoOutPixelFormat: 0 + ps4VideoOutInitialWidth: 1920 + ps4VideoOutBaseModeInitialWidth: 1920 + ps4VideoOutReprojectionRate: 60 + ps4PronunciationXMLPath: + ps4PronunciationSIGPath: + ps4BackgroundImagePath: + ps4StartupImagePath: + ps4StartupImagesFolder: + ps4IconImagesFolder: + ps4SaveDataImagePath: + ps4SdkOverride: + ps4BGMPath: + ps4ShareFilePath: + ps4ShareOverlayImagePath: + ps4PrivacyGuardImagePath: + ps4NPtitleDatPath: + ps4RemotePlayKeyAssignment: -1 + ps4RemotePlayKeyMappingDir: + ps4PlayTogetherPlayerCount: 0 + ps4EnterButtonAssignment: 2 + ps4ApplicationParam1: 0 + ps4ApplicationParam2: 0 + ps4ApplicationParam3: 0 + ps4ApplicationParam4: 0 + ps4DownloadDataSize: 0 + ps4GarlicHeapSize: 2048 + ps4ProGarlicHeapSize: 2560 + playerPrefsMaxSize: 32768 + ps4Passcode: frAQBc8Wsa1xVPfvJcrgRYwTiizs2trQ + ps4pnSessions: 1 + ps4pnPresence: 1 + ps4pnFriends: 1 + ps4pnGameCustomData: 1 + playerPrefsSupport: 0 + enableApplicationExit: 0 + resetTempFolder: 1 + restrictedAudioUsageRights: 0 + ps4UseResolutionFallback: 0 + ps4ReprojectionSupport: 0 + ps4UseAudio3dBackend: 0 + ps4SocialScreenEnabled: 0 + ps4ScriptOptimizationLevel: 2 + ps4Audio3dVirtualSpeakerCount: 14 + ps4attribCpuUsage: 0 + ps4PatchPkgPath: + ps4PatchLatestPkgPath: + ps4PatchChangeinfoPath: + ps4PatchDayOne: 0 + ps4attribUserManagement: 0 + ps4attribMoveSupport: 0 + ps4attrib3DSupport: 0 + ps4attribShareSupport: 0 + ps4attribExclusiveVR: 0 + ps4disableAutoHideSplash: 0 + ps4videoRecordingFeaturesUsed: 0 + ps4contentSearchFeaturesUsed: 0 + ps4attribEyeToEyeDistanceSettingVR: 0 + ps4IncludedModules: [] + monoEnv: + splashScreenBackgroundSourceLandscape: {fileID: 0} + splashScreenBackgroundSourcePortrait: {fileID: 0} + blurSplashScreenBackground: 1 + spritePackerPolicy: + webGLMemorySize: 32 + webGLExceptionSupport: 1 + webGLNameFilesAsHashes: 0 + webGLDataCaching: 1 + webGLDebugSymbols: 0 + webGLEmscriptenArgs: + webGLModulesDirectory: + webGLTemplate: APPLICATION:Default + webGLAnalyzeBuildSize: 0 + webGLUseEmbeddedResources: 0 + webGLCompressionFormat: 0 + webGLLinkerTarget: 1 + webGLThreadsSupport: 0 + webGLWasmStreaming: 0 + scriptingDefineSymbols: {} + platformArchitecture: {} + scriptingBackend: + Standalone: 1 + il2cppCompilerConfiguration: {} + managedStrippingLevel: {} + incrementalIl2cppBuild: {} + allowUnsafeCode: 0 + additionalIl2CppArgs: + scriptingRuntimeVersion: 1 + gcIncremental: 0 + gcWBarrierValidation: 0 + apiCompatibilityLevelPerPlatform: {} + m_RenderingPath: 1 + m_MobileRenderingPath: 1 + metroPackageName: test-standalone-scripts + metroPackageVersion: + metroCertificatePath: + metroCertificatePassword: + metroCertificateSubject: + metroCertificateIssuer: + metroCertificateNotAfter: 0000000000000000 + metroApplicationDescription: test-standalone-scripts + wsaImages: {} + metroTileShortName: + metroTileShowName: 0 + metroMediumTileShowName: 0 + metroLargeTileShowName: 0 + metroWideTileShowName: 0 + metroSupportStreamingInstall: 0 + metroLastRequiredScene: 0 + metroDefaultTileSize: 1 + metroTileForegroundText: 2 + metroTileBackgroundColor: {r: 0.13333334, g: 0.17254902, b: 0.21568628, a: 0} + metroSplashScreenBackgroundColor: {r: 0.12941177, g: 0.17254902, b: 0.21568628, + a: 1} + metroSplashScreenUseBackgroundColor: 0 + platformCapabilities: {} + metroTargetDeviceFamilies: {} + metroFTAName: + metroFTAFileTypes: [] + metroProtocolName: + XboxOneProductId: + XboxOneUpdateKey: + XboxOneSandboxId: + XboxOneContentId: + XboxOneTitleId: + XboxOneSCId: + XboxOneGameOsOverridePath: + XboxOnePackagingOverridePath: + XboxOneAppManifestOverridePath: + XboxOneVersion: 1.0.0.0 + XboxOnePackageEncryption: 0 + XboxOnePackageUpdateGranularity: 2 + XboxOneDescription: + XboxOneLanguage: + - enus + XboxOneCapability: [] + XboxOneGameRating: {} + XboxOneIsContentPackage: 0 + XboxOneEnableGPUVariability: 1 + XboxOneSockets: {} + XboxOneSplashScreen: {fileID: 0} + XboxOneAllowedProductIds: [] + XboxOnePersistentLocalStorageSize: 0 + XboxOneXTitleMemory: 8 + xboxOneScriptCompiler: 1 + XboxOneOverrideIdentityName: + vrEditorSettings: + daydream: + daydreamIconForeground: {fileID: 0} + daydreamIconBackground: {fileID: 0} + cloudServicesEnabled: {} + luminIcon: + m_Name: + m_ModelFolderPath: + m_PortalFolderPath: + luminCert: + m_CertPath: + m_SignPackage: 1 + luminIsChannelApp: 0 + luminVersion: + m_VersionCode: 1 + m_VersionName: + facebookSdkVersion: + facebookAppId: + facebookCookies: 1 + facebookLogging: 1 + facebookStatus: 1 + facebookXfbml: 0 + facebookFrictionlessRequests: 1 + apiCompatibilityLevel: 6 + cloudProjectId: + framebufferDepthMemorylessMode: 0 + projectName: + organizationId: + cloudEnabled: 0 + enableNativePlatformBackendsForNewInputSystem: 0 + disableOldInputManagerSupport: 0 + legacyClampBlendShapeWeights: 0 diff --git a/dist/test-standalone-scripts/ProjectSettings/ProjectVersion.txt b/dist/test-standalone-scripts/ProjectSettings/ProjectVersion.txt new file mode 100644 index 00000000..87079c13 --- /dev/null +++ b/dist/test-standalone-scripts/ProjectSettings/ProjectVersion.txt @@ -0,0 +1,2 @@ +m_EditorVersion: 2019.2.11f1 +m_EditorVersionWithRevision: 2019.2.11f1 (5f859a4cfee5) diff --git a/dist/test-standalone-scripts/ProjectSettings/QualitySettings.asset b/dist/test-standalone-scripts/ProjectSettings/QualitySettings.asset new file mode 100644 index 0000000000000000000000000000000000000000..1398719bf7f029eb444959b4aec3f08bc9f37880 GIT binary patch literal 5100 zcmeI0O^g&p6vtmL%ZDI-izq6C-=eN8yMBPM&VFQpILi(@z#3yhYNu;<8+y9O{@9)6 z;&2ip2M=D*_(5Vk;LXH?@j~K>1dbj!h%v?+!QjDz2gdiWs@|EN>4|r&iq}CwJ5B{a$_qLGgCH#BC z@5|tSaQ!c{9=Y*i<(5+tg6psR|H6Ib{{WO3{8!e3KM0(`e`7897T^@u_5Gc-;12<} zwWPj(u?~D|8$&nYx494gFqCaA;hQbZA8BLgCVYqC+cJ28s4>8tKlEr1zR&Q-l+V^6 z_=o*lhCkjys+Vxv@a@p%6;dLJ?+kO~musOFeu4d;Gkiw|$NK%m@Fy}j;`^F8f9S~` z{71u|0?)SwA-)T2e~mff<4rHslKB5N{FxMO)qiWBwtuz<-)s1DJ@~tZKi`7~hVSgb zKQR1-437GI&YV-A7s2xox+VGlVC-LlIK-p+Os@A!`sn@}!hM}-e z?{B*lNptL<$^Odwbcg@-sr_>|V=Lcozah1MVz1^`2}|89+Cc65=P;|&+@f1N<1Lfl zD=mg<huTToz$u<`y9*e5~gB!amB?D!C3n=Qib7 zT&c*Q(ct`TweC49YJYbhN8iuJnunb7#eXdQ3^%iL2?{T6z9=96Bc* z4EjSuLn^tp6(a<#g8S{H`%PvKW1x-}D4BqdGt8=;n9%T?hOx zezU}xqcrlb^1Fe!i(7v85huT!+?Uf?tMfBAX*5PJ^1D5O%!vFRVKhF!JB;RZ)|3S1 zd{;5$_i7O>7~)~J3ZXf{R{yZ`{)lkLt7)lo_(hc_n(aYPqpsxXa54eSgP;< literal 0 HcmV?d00001 diff --git a/dist/test-standalone-scripts/ProjectSettings/TimeManager.asset b/dist/test-standalone-scripts/ProjectSettings/TimeManager.asset new file mode 100644 index 0000000000000000000000000000000000000000..8ca1c3bfadd8537d234802e8723b147f90e43188 GIT binary patch literal 4116 zcmZQzU@T-{5D;NtU=Rk2cRHG4(uHy1tFQaslJJMiRr0D z3~refsVNE|k>Zln0tVm2ip<>7Tm{FRocwZRiQIUQn&9NboK(1?fW)Gb%;cO@gm#E8 zk^PD-e2|m563HN-M;d3X7KBsH}8@!-(Nj#ynlU(&@)7Vg`HuHQ%GApaN&hB zsK@hVjK4G(?;!;JRSY_SpQ6~i!~-t@%$VXg5(a)5I1^49_-mAZH$6fh8jQn9Gg!%} z5`qGLIl+dRl4w)LkHl@G9Ej}|n9koI{yr7x_Z4`lUnU&+S0I$bNz3c^6WmFE0DKyY z3ggS_uPpFV{~Y1aUxgqJCoQkPn&3|QYrv{#Qv%ETL+j+4xaS2{iDO08-I_se;Y&$BBdELs_goybF{>lwDY$KgsE8Q zm&EyfKoI7)d5ID&@HoFu=n3n#1*pm4g<<|rsfB{Gy%i{jlcwU;`5#t9j#uN}2HZ?Z zs*wK$)hKv3wo~bpe+T%CDSefHXNE&l@oz<&v0VU7IBC%TKur|v?{2^xPMV5W^V_3{ z9Ix{4{Rgkcw-3Cz)Cu|V{(mM2`ujm~4lgW!ehv_7m|T)*GyVJ=6qwFu&(EO(FZF*A z4*kPachWzC_zwC>DZe(cy#safEf4|44z?=a2W05(p0m{aq#b z{X_l=KrR1rvHs5p1%INz_iFEN6**0gUcwCeqGrFpCkfT(*QdR|qXk~-56~Ha{wZYD z#;5C_MtleTu|j@bpMK|Z^PV3)aef`J5r?IH=`+vT`8*G>Y zP!M+kK^#t6{{6cM*pwXnZQn~CO$NU2+R2=oB%a@h*}}bBZk$X9j!VtjqU$F!P3Lyh zR8O)=QFX5&+vtCtQi+(!$cpE0x?vC@T_)>P(74>H*WGB&dyLeRR+zLRx8`_B5Y-;q zZkTw1AIm|_wjz7pinAHSU?+U5`6%)1IFlICp4FtRGyYs+MM*1^NhTbvTM%Eh7ToI& zX;1i06nM^`XW78qQ)t$W>Or(%`Ljs%za9=&nF_1-dKHU4j2)1#q97 zPm(YmuT-qiE4OT`Y&V0JQ*PmAFzS@;V4<>Dt+==b#0>Xe)!%l)j(zuM`-;oL-_v8Q ba4%PRL;B?%k$T1a=uD>nPfjmClG6Ndr2C=% literal 0 HcmV?d00001 diff --git a/dist/test-standalone-scripts/ProjectSettings/VFXManager.asset b/dist/test-standalone-scripts/ProjectSettings/VFXManager.asset new file mode 100644 index 0000000000000000000000000000000000000000..4e92658d745a7450c7b1203aec7ec9588d0a5b41 GIT binary patch literal 4148 zcmeHGOG?8~6ur;*|BHeH2M(N^(^7z8x&^p}D^28MBnU5lxQ2^B$Ad=xqaeQ@4EMKZ zxPkRC?2EuS&1u+kX)B zU;aZqdi4DJ;Mt#_Azr`zM?i>e~osap8(8H_UMA{pN@K1XKbl W0hNGCKqc^>6S%&*T77xmUi$)2<8%)I literal 0 HcmV?d00001 diff --git a/dist/test-standalone-scripts/ProjectSettings/XRSettings.asset b/dist/test-standalone-scripts/ProjectSettings/XRSettings.asset new file mode 100644 index 00000000..482590c1 --- /dev/null +++ b/dist/test-standalone-scripts/ProjectSettings/XRSettings.asset @@ -0,0 +1,10 @@ +{ + "m_SettingKeys": [ + "VR Device Disabled", + "VR Device User Alert" + ], + "m_SettingValues": [ + "False", + "False" + ] +} \ No newline at end of file diff --git a/src/command-options/docker-test-options.ts b/src/command-options/docker-test-options.ts new file mode 100644 index 00000000..a218d2f8 --- /dev/null +++ b/src/command-options/docker-test-options.ts @@ -0,0 +1,198 @@ +import type { YargsInstance } from '../dependencies.ts'; +import { IOptions } from './options-interface.ts'; +import * as os from 'node:os'; + +function defaultDockerMemoryLimit(): string { + const bytesInMegabyte = 1024 * 1024; + + let memoryMultiplier: number; + switch (os.platform()) { + case 'linux': + memoryMultiplier = 0.95; + break; + case 'win32': + memoryMultiplier = 0.8; + break; + default: + memoryMultiplier = 0.75; + break; + } + + return `${Math.floor((os.totalmem() / bytesInMegabyte) * memoryMultiplier)}m`; +} + +/** + * Options for `game-ci test`'s classic Docker/Hub-image-driven test flow - + * the same batchmode `-runTests` invocation unity-test-runner's action uses, + * as an alternative to the `unityCliArgs` passthrough to Unity's own + * experimental `unity test` CLI (see UnityTestOptions). Duplicates a handful + * of docker/runtime flags from BuildOptions rather than sharing that module + * directly - BuildOptions.configure() demands `targetPlatform`, which tests + * don't need. + */ +export class DockerTestOptions implements IOptions { + public static configure(yargs: YargsInstance): void { + yargs + .option('docker', { + description: String.dedent` + Run the classic Docker/Hub-image-driven Unity batchmode test flow + instead of Unity's own experimental \`unity test\` CLI. Requires a + Unity Editor Docker image (see customImage/containerRegistry*).`, + type: 'boolean', + demandOption: false, + default: false, + }) + .option('local', { + description: String.dedent` + Only meaningful with --docker. Run the same batchmode test flow + directly on this machine instead of inside a container - for + self-hosted runners with Unity already installed and licensed. + Mirrors the orchestrator's own local (host) provider.`, + type: 'boolean', + demandOption: false, + default: false, + }) + .option('testPlatforms', { + description: String.dedent` + Semicolon-separated test platforms to run, e.g. "playmode;editmode". + Use "COMBINE_RESULTS" as an extra entry to merge prior platforms' + results into one summary, and "standalone" to build and run tests + in a standalone player instead of in-editor.`, + type: 'string', + demandOption: false, + default: 'playmode;editmode', + }) + .option('artifactsPath', { + description: 'Path (relative to the project) to write test result XML and logs to.', + type: 'string', + demandOption: false, + default: 'artifacts', + }) + .option('coverageEnabled', { + description: String.dedent` + Enable Unity's code coverage instrumentation during the test run. + Some Unity versions/configurations fail or crash with this on - + see game-ci/unity-test-runner#302, #306, #301. Set to false to + omit -enableCodeCoverage/-coverageOptions/-coverageResultsPath + entirely.`, + type: 'boolean', + demandOption: false, + default: true, + }) + .option('coverageResultsPath', { + description: 'Path (relative to the project) to write code coverage results to.', + type: 'string', + demandOption: false, + default: 'CodeCoverage', + }) + .option('coverageOptions', { + description: "Additional arguments to pass to Unity's code coverage package.", + type: 'string', + demandOption: false, + default: '', + }) + .option('packageMode', { + description: String.dedent` + Test a Unity package (projectPath points at the package folder, + not a full project) by creating a throwaway project and adding + the package as a local file dependency.`, + type: 'boolean', + demandOption: false, + default: false, + }) + .option('packageName', { + description: 'Package name to add to the throwaway project. Required when packageMode is set.', + type: 'string', + demandOption: false, + default: '', + }) + .option('scopedRegistryUrl', { + description: 'Scoped registry URL to add to the throwaway project when in packageMode.', + type: 'string', + demandOption: false, + default: '', + }) + .option('registryScopes', { + description: 'Comma-separated scopes for scopedRegistryUrl.', + type: 'string', + demandOption: false, + default: '', + }) + .option('privateRegistryToken', { + description: 'Auth token for a private UPM registry, written to .upmconfig.toml before testing.', + type: 'string', + demandOption: false, + default: '', + }) + .option('privateRegistryUser', { + description: 'Username for a private UPM registry that requires one even with token auth (e.g. Azure DevOps).', + type: 'string', + demandOption: false, + default: '', + }) + .option('runAsHostUser', { + description: String.dedent`Linux only. Run as a user matching the host system's UID/GID instead of the + container's default root user, so artifacts aren't left root-owned on the host.`, + type: 'boolean', + demandOption: false, + default: false, + }) + .option('useHostNetwork', { + description: 'Linux only. Initialises Docker using the host network.', + type: 'boolean', + demandOption: false, + default: false, + }) + .option('dockerCpuLimit', { + description: 'Number of CPU cores to assign the docker container. Defaults to all available cores.', + type: 'string', + demandOption: false, + default: os.cpus().length.toString(), + }) + .option('dockerMemoryLimit', { + description: String.dedent`Amount of memory to assign the docker container. Defaults to 95% of total system + memory rounded down to the nearest megabyte on Linux and 80% on Windows.`, + type: 'string', + demandOption: false, + default: defaultDockerMemoryLimit(), + }) + .option('dockerShmSize', { + description: String.dedent`Size of /dev/shm to assign the docker container, using the format + (m or g). Some Unity versions/configurations fail with "Insufficient shared memory available" against + Docker's 64m default - see game-ci/unity-test-runner#307/#308.`, + type: 'string', + demandOption: false, + default: '', + }) + .option('dockerIsolationMode', { + description: 'Windows only. Isolation mode for the docker container: process, hyperv, or default.', + type: 'string', + demandOption: false, + default: 'default', + }) + .option('containerRegistryRepository', { + description: 'Container registry and repository to pull the Unity editor image from. Only applies if customImage is not set.', + type: 'string', + demandOption: false, + default: 'unityci/editor', + }) + .option('containerRegistryImageVersion', { + description: 'Container registry image rolling version. Only applies if customImage is not set.', + type: 'string', + demandOption: false, + default: '3', + }) + .option('sshPublicKeysDirectoryPath', { + description: 'Path to a directory containing SSH public keys to forward to the container.', + type: 'string', + demandOption: false, + default: '', + }) + .option('dockerWorkspacePath', { + description: 'The path to mount the workspace inside the docker container.', + type: 'string', + demandOption: false, + default: '/github/workspace', + }); + } +} diff --git a/src/command/test/unity-test-command.test.ts b/src/command/test/unity-test-command.test.ts index 2b7dfed8..5833e696 100644 --- a/src/command/test/unity-test-command.test.ts +++ b/src/command/test/unity-test-command.test.ts @@ -1,15 +1,24 @@ import { describe, it, expect, mock, afterEach } from 'bun:test'; import { UnityTestCommand } from './unity-test-command.ts'; import { UnityCliAdapter } from '../../model/unity-cli-adapter.ts'; +import { Docker } from '../../model/index.ts'; +import { HostRunner } from '../../model/host-runner.ts'; +import { PlatformSetup } from '../../logic/unity/platform-setup/index.ts'; const originalIsAvailable = UnityCliAdapter.isAvailable; const originalTest = UnityCliAdapter.test; +const originalDockerRun = Docker.run; +const originalHostRunnerRun = HostRunner.run; +const originalPlatformSetup = PlatformSetup.setup; afterEach(() => { - // Both are shared statics — restore them so other test files that - // exercise the real UnityCliAdapter aren't affected by this file's mocks. + // All statics — restore them so other test files that exercise the real + // implementations aren't affected by this file's mocks. UnityCliAdapter.isAvailable = originalIsAvailable; UnityCliAdapter.test = originalTest; + Docker.run = originalDockerRun; + HostRunner.run = originalHostRunnerRun; + PlatformSetup.setup = originalPlatformSetup; }); describe('UnityTestCommand', () => { @@ -52,4 +61,52 @@ describe('UnityTestCommand', () => { await expect(command.execute({} as any)).rejects.toThrow(/test:.*failed.*unity wrote to stderr/); }); + + it('--docker runs the classic batchmode flow via Docker.run, not the unity CLI', async () => { + const isAvailableMock = mock(() => Promise.resolve(true)); + UnityCliAdapter.isAvailable = isAvailableMock; + PlatformSetup.setup = mock(() => Promise.resolve()); + const dockerRunMock = mock(() => Promise.resolve()); + Docker.run = dockerRunMock; + + const command = new UnityTestCommand('test'); + const result = await command.execute({ + docker: true, + hostPlatform: 'linux', + engineVersion: '2022.3.20f1', + } as any); + + expect(result).toBe(true); + expect(isAvailableMock).not.toHaveBeenCalled(); + expect(dockerRunMock).toHaveBeenCalledTimes(1); + const [image, options] = dockerRunMock.mock.calls[0] as unknown as [string, any]; + expect(image).toContain('base'); // NoTarget resolves to the 'base' editor image + expect(options.runTests).toBe(true); + }); + + it('--docker --local runs directly on the host via HostRunner.run, not Docker', async () => { + const dockerRunMock = mock(() => Promise.resolve()); + Docker.run = dockerRunMock; + const hostRunnerMock = mock(() => Promise.resolve()); + HostRunner.run = hostRunnerMock; + + const command = new UnityTestCommand('test'); + const result = await command.execute({ docker: true, local: true, hostPlatform: 'linux' } as any); + + expect(result).toBe(true); + expect(dockerRunMock).not.toHaveBeenCalled(); + expect(hostRunnerMock).toHaveBeenCalledTimes(1); + const [options] = hostRunnerMock.mock.calls[0] as unknown as [any]; + expect(options.runTests).toBe(true); + }); + + it('--docker on macOS throws instead of trying to pull a nonexistent macOS editor image', async () => { + PlatformSetup.setup = mock(() => Promise.resolve()); + + const command = new UnityTestCommand('test'); + + await expect( + command.execute({ docker: true, hostPlatform: 'darwin', engineVersion: '2022.3.20f1' } as any), + ).rejects.toThrow(/macOS/i); + }); }); diff --git a/src/command/test/unity-test-command.ts b/src/command/test/unity-test-command.ts index 43dd2511..bdc20563 100644 --- a/src/command/test/unity-test-command.ts +++ b/src/command/test/unity-test-command.ts @@ -2,22 +2,41 @@ import { CommandInterface } from '../command-interface.ts'; import { CommandBase } from '../command-base.ts'; import { UnityCliAdapter } from '../../model/unity-cli-adapter.ts'; import { UnityTestOptions } from '../../command-options/unity-test-options.ts'; +import { DockerTestOptions } from '../../command-options/docker-test-options.ts'; +import { ProjectOptions } from '../../command-options/project-options.ts'; +import { UnityOptions } from '../../command-options/unity-options.ts'; +import { Docker, RunnerImageTag } from '../../model/index.ts'; +import { HostRunner } from '../../model/host-runner.ts'; +import { PlatformSetup } from '../../logic/unity/platform-setup/index.ts'; import type { YargsInstance, Options } from '../../dependencies.ts'; /** - * `game-ci test` — runs Unity's own official test runner via Unity CLI's - * `test` command (docs.unity.com/en-us/unity-cli, still experimental - * upstream), as an alternative to unity-test-runner's Docker/Hub-driven - * flow. See game-ci/roadmap#11 workstream 3 and game-ci/cli#58. + * `game-ci test` — two independent ways to run Unity tests, chosen via + * --docker: * - * Requires the `unity` CLI binary on PATH. Unity's own docs don't publish - * a flag table for `test` (unlike `install`/`install-modules`) — they - * explicitly point to `unity test --help` on the installed binary as the - * authoritative reference. So this command doesn't invent or guess at flags: - * everything after --unityCliArgs is passed through to `unity test` verbatim. + * - Default (--docker not set): wraps Unity's own official, still + * experimental `test` CLI command (docs.unity.com/en-us/unity-cli). + * Requires the `unity` binary on PATH. See game-ci/roadmap#11 + * workstream 3 and game-ci/cli#58. + * + * - --docker: the classic Docker/Hub-image-driven batchmode test flow + * (-runTests) unity-test-runner's action uses today - editmode/playmode/ + * standalone/package-mode testing, coverage, artifact collection. Add + * --local to run the same flow directly on this machine instead of in a + * container, for self-hosted runners with Unity already installed (see + * HostRunner) - mirrors orchestrator's own local (host) provider vs its + * docker provider. */ export class UnityTestCommand extends CommandBase implements CommandInterface { public async execute(options: Options): Promise { + if (options.docker) { + return this.executeDocker(options); + } + + return this.executeUnityCli(options); + } + + private async executeUnityCli(options: Options): Promise { const extraArgs = String(options.unityCliArgs || '') .split(' ') .map((arg) => arg.trim()) @@ -27,7 +46,8 @@ export class UnityTestCommand extends CommandBase implements CommandInterface { if (!available) { throw new Error( "test: requires Unity's official `unity` CLI binary on PATH " + - '(https://docs.unity.com/en-us/unity-cli). Not found in this environment.', + '(https://docs.unity.com/en-us/unity-cli). Not found in this environment, or pass --docker ' + + 'to run the classic Docker/Hub-image-driven test flow instead.', ); } @@ -44,7 +64,62 @@ export class UnityTestCommand extends CommandBase implements CommandInterface { } } + private async executeDocker(options: Options): Promise { + const { hostPlatform, local } = options; + + if (local) { + await HostRunner.run({ ...options, runTests: true }); + return true; + } + + // Docker test mode is currently only wired up for Linux containers - + // dist/platforms/ubuntu/steps/test.sh + runsteps.sh's RUN_TESTS branch. + // Windows' entrypoint.ps1 doesn't know about RUN_TESTS yet (it always + // runs build.ps1), so running this on a Windows host today would + // silently attempt a BUILD instead of a test rather than failing + // loudly - reject it explicitly instead. macOS has no Unity Editor + // Docker images at all. Checked before PlatformSetup.setup runs, so + // this fails fast instead of after prompting for credentials. + if (hostPlatform !== 'linux') { + throw new Error( + `--docker's classic batchmode test flow is currently only supported on Linux hosts/containers ` + + `(got hostPlatform=${hostPlatform}). ${ + hostPlatform === 'darwin' + ? 'No Unity Editor Docker images exist for macOS - omit --docker to use the native `unity test` CLI instead.' + : 'Windows Docker test support is tracked separately (the container-side scripts only handle builds so far).' + }`, + ); + } + + // No target platform is being built - resolves to the 'base' editor + // image, same one activate/NoTarget already uses (see RunnerImageTag's + // targetPlatformSuffixes.noTarget). UnityOptions.configure() defaults + // targetPlatform to StandaloneWindows64 (build's default, not test's) - + // overridden back to NoTarget in configureOptions() below, so this + // should already be NoTarget unless a caller explicitly passed one. + const testOptions = { ...options, targetPlatform: options.targetPlatform || 'NoTarget' }; + + const image = new RunnerImageTag(testOptions); + if (log.isVerbose) log.debug('Using image:', image); + + await PlatformSetup.setup(testOptions); + + await log.group('Unity test', async () => { + await Docker.run(image.toString(), { ...testOptions, runTests: true }); + }); + + return true; + } + public async configureOptions(yargs: YargsInstance): Promise { + await ProjectOptions.configure(yargs); + await UnityOptions.configure(yargs); + // UnityOptions defaults targetPlatform to StandaloneWindows64 (a build + // concern) - tests don't build anything, so default to NoTarget's + // 'base' editor image instead. A caller can still pass --targetPlatform + // explicitly (e.g. for --testPlatforms=standalone scenarios). + yargs.default('targetPlatform', 'NoTarget'); await UnityTestOptions.configure(yargs); + await DockerTestOptions.configure(yargs); } } diff --git a/src/logic/unity/environment.ts b/src/logic/unity/environment.ts index 930eae80..a2bc5a0c 100644 --- a/src/logic/unity/environment.ts +++ b/src/logic/unity/environment.ts @@ -35,6 +35,22 @@ const UnityEnvironment = { { name: 'RUN_AS_HOST_USER', value: options.runAsHostUser ? 'true' : '' }, { name: 'ENABLE_GPU', value: options.enableGpu ? 'true' : '' }, { name: 'GIT_CONFIG_EXTENSIONS', value: options.gitConfigExtensions }, + // Consumed by dist/platforms/*/steps/runsteps.sh + test.sh (`game-ci + // test --docker`, see UnityTestCommand) - a no-op for build/activate + // since these are all empty/undefined there and getEnvVarString drops + // empty values. + { name: 'RUN_TESTS', value: options.runTests ? 'true' : '' }, + { name: 'TEST_PLATFORMS', value: options.testPlatforms }, + { name: 'ARTIFACTS_PATH', value: options.artifactsPath }, + { name: 'COVERAGE_RESULTS_PATH', value: options.coverageResultsPath }, + { name: 'COVERAGE_OPTIONS', value: options.coverageOptions }, + { name: 'COVERAGE_ENABLED', value: options.coverageEnabled === false ? 'false' : 'true' }, + { name: 'PACKAGE_MODE', value: options.packageMode ? 'true' : '' }, + { name: 'PACKAGE_NAME', value: options.packageName }, + { name: 'SCOPED_REGISTRY_URL', value: options.scopedRegistryUrl }, + { name: 'REGISTRY_SCOPES', value: options.registryScopes }, + { name: 'PRIVATE_REGISTRY_TOKEN', value: options.privateRegistryToken }, + { name: 'PRIVATE_REGISTRY_USER', value: options.privateRegistryUser }, ] as DockerParameter[]; }, }; diff --git a/src/model/host-runner.ts b/src/model/host-runner.ts new file mode 100644 index 00000000..535802a1 --- /dev/null +++ b/src/model/host-runner.ts @@ -0,0 +1,78 @@ +import type { Options } from '../dependencies.ts'; +import { System } from './system/system.ts'; +import { ImageEnvironmentFactory } from './image-environment-factory.ts'; +import { UnityEnvironment } from '../logic/unity/environment.ts'; +import { path, fsSync as fs } from '../dependencies.ts'; + +/** + * Runs the same activate/test/build step scripts Docker.run mounts into a + * container, directly on this machine instead - for self-hosted runners + * with Unity already installed. Mirrors MacBuilder (macOS never had a + * Docker path to begin with), generalized to Linux/Windows so `--local` + * (see DockerTestOptions) works cross-platform. Mirrors orchestrator's own + * `local` provider, which runs the equivalent build/test commands directly + * on the host rather than dispatching to a container/cloud provider. + * + * Deliberately does NOT invoke entrypoint.sh: that script does + * container-only setup (randomizing /etc/machine-id, creating a matching + * host user via useradd/groupadd) that would mutate a real, persistent + * self-hosted machine rather than a throwaway container - genuinely + * dangerous outside Docker. Goes straight to runsteps.sh (activate -> test + * or build -> return license), the same core steps.sh scripts already use. + */ +class HostRunner { + private static buildEnv(options: Options): Record { + const { currentWorkDir, cliDistPath, hostPlatform } = options; + const extraVariables = options.engine === 'unity' ? UnityEnvironment.getVariables(options) : []; + const variables = ImageEnvironmentFactory.getEnvironmentVariables(options, extraVariables); + const env: Record = {}; + for (const { name, value } of variables) { + if (value === '' || value === undefined) continue; + env[name] = value.toString(); + } + if (currentWorkDir) env.GITHUB_WORKSPACE = currentWorkDir; + + const stepsDir = HostRunner.stepsDir(hostPlatform, cliDistPath); + env.STEPS_DIR = stepsDir; + env.TEST_RUNNER_ACTION_DIR = path.join(cliDistPath, 'test-standalone-scripts'); + + // entrypoint.sh normally creates this before sourcing runsteps.sh - + // replicated here since HostRunner bypasses entrypoint.sh entirely. + const activateLicensePath = path.join(currentWorkDir || process.cwd(), '_activate-license~'); + if (!fs.existsSync(activateLicensePath)) fs.mkdirSync(activateLicensePath, { recursive: true }); + env.ACTIVATE_LICENSE_PATH = activateLicensePath; + + return env; + } + + private static stepsDir(hostPlatform: string, cliDistPath: string): string { + switch (hostPlatform) { + case 'win32': + return path.join(cliDistPath, 'platforms', 'windows'); + case 'linux': + default: + return path.join(cliDistPath, 'platforms', 'ubuntu', 'steps'); + } + } + + public static async run(options: Options, silent = false) { + const { cliDistPath, hostPlatform } = options; + + if (hostPlatform !== 'linux') { + throw new Error( + `--local is currently only supported when running on Linux (got hostPlatform=${hostPlatform}). ` + + 'macOS already runs natively via MacBuilder without needing --local; Windows host-mode support is tracked separately.', + ); + } + + log.warning('running the process directly on this host (no Docker)'); + + const runstepsPath = path.join(cliDistPath, 'platforms', 'ubuntu', 'steps', 'runsteps.sh'); + await System.run(`bash "${runstepsPath}"`, undefined, { + silent, + env: HostRunner.buildEnv(options), + }); + } +} + +export { HostRunner };