Search Unity

Question Running standalone tests from the command line with arguments for the built player

Discussion in 'Testing & Automation' started by nratcliff, Apr 24, 2023.

  1. nratcliff

    nratcliff

    Joined:
    Jul 5, 2014
    Posts:
    67
    I'm working on implementing performance tests in a project that requires some command line arguments to be passed to the built player in order to run in a test environment (i.e.
    MyGame.exe -devEnv
    . The tests are run in a CI environment so everything needs to be automated.

    Is there any way to pass command line arguments to the built player when running standalone tests from the command line?
     
  2. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    5,990
    it‘s possible to pass parameters according to the GameCI docs. believe you have to use a static method that you call from the command line that then performs the tests and scans for cmd args. Try searching for GameCI „BuildScript“ for an example.
     
  3. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,659
    Your best bet is probably to do the 'split build and run' so that you take over responsibility for running the player (instead of having the Editor do it).
     
    nratcliff likes this.
  4. nratcliff

    nratcliff

    Joined:
    Jul 5, 2014
    Posts:
    67
    Thanks for clarifying! That's the key I needed to get things running.

    For future wary travelers with a similar question, here's what I ended up writing:

    Code (CSharp):
    1. [assembly:TestPlayerBuildModifier(typeof(SplitTestRunner))]
    2. [assembly:PostBuildCleanup(typeof(SplitTestRunner))]
    3. namespace TestProject.Editor
    4. {
    5.     public class SplitTestRunner : ITestPlayerBuildModifier, IPostBuildCleanup
    6.     {
    7.         private static bool runningPlayerTests;
    8.         private static string playerPath;
    9.      
    10.         public BuildPlayerOptions ModifyOptions(BuildPlayerOptions playerOptions)
    11.         {
    12.             // don't auto-run the player
    13.             playerOptions.options &= ~BuildOptions.AutoRunPlayer;
    14.          
    15.             // get command line args (see UTF source for CommandLineOption)
    16.             string buildPath = null;
    17.             CommandLineProcessor.Process(
    18.                 new CommandLineOption("buildPath", val => buildPath = val)
    19.             );
    20.  
    21.             // assign build path
    22.             if (!string.IsNullOrEmpty(buildPath))
    23.             {
    24.                 buildPath = Path.GetFullPath(buildPath);
    25.              
    26.                 var fileName = Path.GetFileName(playerOptions.locationPathName);
    27.                 if (!string.IsNullOrEmpty(fileName))
    28.                 {
    29.                     buildPath = Path.Combine(buildPath, fileName);
    30.                 }
    31.  
    32.                 playerOptions.locationPathName = buildPath;
    33.             }
    34.  
    35.             runningPlayerTests = true;
    36.          
    37.             // NOTE: the built player path will be a directory for mac but an executable on Win.
    38.             // TODO: We'll have to do a little special handling here.
    39.             playerPath = Path.Combine(playerOptions.locationPathName, "Contents/MacOS/", PlayerSettings.productName);
    40.          
    41.             return playerOptions;
    42.         }
    43.  
    44.         public void Cleanup()
    45.         {
    46.             if (!runningPlayerTests) return;
    47.          
    48.             var runTests = false;
    49.             CommandLineProcessor.Process(new CommandLineOption("runTests", _ => runTests = true));
    50.  
    51.             if (!runTests) return;
    52.  
    53.             // run the test player next frame (after other build cleanup steps have a chance to run)
    54.             EditorApplication.update += RunTestPlayer;
    55.         }
    56.  
    57.         private void RunTestPlayer()
    58.         {
    59.             // find built player
    60.             if (!File.Exists(playerPath))
    61.             {
    62.                 Debug.LogError($"Could not find standalone test player at path {playerPath}. Aborting run.");
    63.                 EditorApplication.Exit(1);
    64.             }
    65.          
    66.             // get command line arguments for player
    67.             var commandLineArgs = string.Empty;
    68.             CommandLineProcessor.Process(new CommandLineOption("addTestFlag", _ => commandLineArgs = "-test"));
    69.  
    70.             // run player with command line args
    71.             var proc = new Process
    72.             {
    73.                 StartInfo =
    74.                 {
    75.                     FileName = playerPath,
    76.                     Arguments = commandLineArgs,
    77.                 }
    78.             };
    79.          
    80.             if (!proc.Start())
    81.             {
    82.                 Debug.LogError($"Failed to start test player process at path {playerPath}. Aborting.");
    83.                 EditorApplication.Exit(1);
    84.             }
    85.          
    86.             EditorApplication.update -= RunTestPlayer;
    87.  
    88.             // if the test player successfully started, then the rest will be handled by the test framework!
    89.         }
    90.     }
    91. }
    CommandLineProcessor
    is a little custom thing I wrote that basically works similar to UTF's
    CommandLineOptionSet
    . The UTF CLI processing stuff is internal so we can't just use it but it's easy enough to reference the source and write your own.

    I'm running the tests with a simple little bash script at the project root:

    Code (bash):
    1. #!/bin/bash
    2.  
    3. SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
    4. UVERS="2021.3.18f1"
    5.  
    6. "$UNITY_DIR/$UVERS/Unity.app/Contents/MacOS/unity" \
    7.     -runTests \
    8.     -batchmode \
    9.     -nographics \
    10.     -silent-crashes \
    11.     -projectPath "$SCRIPT_DIR" \
    12.     -testResults "$SCRIPT_DIR/test-results.xml" \
    13.     -testFilter "PlayMode" \
    14.     -testPlatform StandaloneOSX \
    15.     -buildPath "$SCRIPT_DIR/Builds/automated/test" \
    16.     -addTestFlag    
    You can see that I'm basically just using the normal
    -runTests
    and
    -testPlatform
    flags here.
    SplitTestRunner
    handles the rest.
     
  5. wzy981114

    wzy981114

    Joined:
    Apr 17, 2023
    Posts:
    1
    Are you using the command line to build the standalone tests? I'm very confused about how to do this.