test_installer.py 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. """
  2. Copyright (c) Contributors to the Open 3D Engine Project.
  3. For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. SPDX-License-Identifier: Apache-2.0 OR MIT
  5. Pytest test configuration file.
  6. Example command to run these tests:
  7. pytest cmake/Platform/Windows/Packaging/Tests \
  8. --capture=no \
  9. --log-file=C:/workspace/test_installer.log \
  10. --installer-uri=file:///C:/path/to/o3de_installer.exe \
  11. --install-root=C:/O3DE/0.0.0.0 \
  12. --project-path=C:/workspace/TestProject
  13. Alternately, the installer-uri can be an s3 or web URL. For example:
  14. --installer-uri=s3://bucketname/o3de_installer.exe
  15. --installer-uri=https://o3de.binaries.org/o3de_installer.exe
  16. """
  17. import pytest
  18. import shutil
  19. from pathlib import Path
  20. from subprocess import TimeoutExpired
  21. from o3de import manifest
  22. @pytest.fixture(scope="session")
  23. def test_installer_fixture(context):
  24. """ Installer executable succeeds. """
  25. assert context.installer_path.is_file(), f"Invalid installer path {context.installer_path}"
  26. # when the installer is run with timeout of 30 minutes
  27. result = context.run([str(context.installer_path) , f"InstallFolder={context.install_root}", "/quiet"], timeout=30*60)
  28. # the installer succeeds
  29. assert result.returncode == 0, f"Installer failed with exit code {result.returncode}"
  30. # the install root is created
  31. assert context.install_root.is_dir(), f"Invalid install root {context.install_root}"
  32. @pytest.mark.parametrize(
  33. "filename", [
  34. pytest.param('Editor.exe'),
  35. pytest.param('AssetProcessor.exe'),
  36. pytest.param('MaterialEditor.exe'),
  37. pytest.param('o3de.exe'),
  38. ]
  39. )
  40. def test_binaries_exist(test_installer_fixture, context, filename):
  41. """ Installer key binaries exist. """
  42. assert (context.engine_bin_path / filename).is_file(), f"{filename} not found in {context.engine_bin_path}"
  43. @pytest.fixture(scope="session")
  44. def test_o3de_registers_engine_fixture(test_installer_fixture, context):
  45. """ Engine is registered when o3de.exe is run. """
  46. try:
  47. result = context.run([str(context.engine_bin_path / 'o3de.exe')], timeout=15)
  48. # o3de.exe should not close with an error code
  49. assert result.returncode == 0, f"o3de.exe failed with exit code {result.returncode}"
  50. except TimeoutExpired as e:
  51. # we expect to close the app ourselves
  52. pass
  53. # a valid engine.json exists
  54. engine_json_data = manifest.get_engine_json_data(engine_name=None, engine_path=context.install_root)
  55. assert engine_json_data, f"Failed to get engine.json data for engine in {context.install_root}"
  56. assert 'engine_name' in engine_json_data, "Engine.json does not contain engine_name key"
  57. # the engine is registered
  58. engine_name = engine_json_data['engine_name']
  59. engine_registered_path = manifest.get_registered(engine_name = engine_name)
  60. assert engine_registered_path, f"Failed to get registered engine path for {engine_name}"
  61. assert engine_registered_path.resolve() == context.install_root, f"{engine_name} is registered to {engine_registered_path} instead of {context.install_root}"
  62. @pytest.fixture(scope="session")
  63. def test_create_project_fixture(test_o3de_registers_engine_fixture, context):
  64. """ o3de.bat CLI creates a project. """
  65. o3de_path = context.install_root / 'scripts/o3de.bat'
  66. result = context.run([str(o3de_path),'create-project','--project-path', str(context.project_path)])
  67. assert result.returncode == 0, f"o3de.bat failed to create a project with exit code {result.returncode}"
  68. project_json_path = Path(context.project_path) / 'project.json'
  69. assert project_json_path.is_file(), f"No project.json found at {project_json_path}"
  70. @pytest.fixture(scope="session")
  71. def test_compile_project_fixture(test_create_project_fixture, context):
  72. """ Project can be configured and compiled. """
  73. project_name = Path(context.project_path).name
  74. cmake_path = next(context.cmake_runtime_path.glob('**/cmake.exe'))
  75. launcher_target = f"{project_name}.GameLauncher"
  76. # configure non-monolithic
  77. result = context.run([str(cmake_path),'-B', str(context.project_build_path_profile), '-S', '.'], cwd=context.project_path)
  78. assert result.returncode == 0, 'Failed to configure the test project non-monolithic build'
  79. assert (context.project_build_path_profile / f'{project_name}.sln').is_file(), 'No project solution file was created'
  80. # build profile (non-monolithic)
  81. result = context.run([str(cmake_path),'--build', str(context.project_build_path_profile), '--target', launcher_target, 'Editor', '--config', 'profile','--','-m'], cwd=context.project_path)
  82. assert result.returncode == 0, 'Failed to build the test project profile non-monolithic Launcher and Editor targets'
  83. assert (context.project_bin_path_profile / f'{launcher_target}.exe').is_file(), 'No test project binary was created'
  84. # configure monolithic
  85. result = context.run([str(cmake_path),'-B', str(context.project_build_path_release), '-S', '.','-DLY_MONOLITHIC_GAME=1'], cwd=context.project_path)
  86. assert result.returncode == 0, 'Failed to configure the test project monolithic build'
  87. assert (context.project_build_path_release / f'{project_name}.sln').is_file(), 'No project solution file was created'
  88. # build release (monolithic)
  89. result = context.run([str(cmake_path),'--build', str(context.project_build_path_release), '--target', launcher_target, '--config', 'release','--','-m'], cwd=context.project_path)
  90. assert result.returncode == 0, 'Failed to build the test project monolithic release Launcher target'
  91. assert (context.project_bin_path_release / f'{launcher_target}.exe').is_file(), 'No test project binary was created'
  92. @pytest.fixture(scope="session")
  93. def test_run_asset_processor_batch_fixture(test_compile_project_fixture, context):
  94. """ AssetProcessorBatch can process assets before running Editor and Launcher tests which use them. """
  95. try:
  96. result = context.run([str(context.engine_bin_path / 'AssetProcessorBatch.exe'),f'--project-path="{context.project_path}"','/platform=pc'], timeout=30*60)
  97. assert result.returncode == 0, f"AssetProcessorBatch.exe failed with exit code {result.returncode}"
  98. except TimeoutExpired as e:
  99. pass
  100. @pytest.fixture(scope="session")
  101. def test_run_editor_fixture(test_run_asset_processor_batch_fixture, context):
  102. """ Editor can be run without crashing. """
  103. try:
  104. # run Editor.exe for 2 mins
  105. cmd = [
  106. str(context.engine_bin_path / 'Editor.exe'),
  107. f'--project-path="{context.project_path}"',
  108. '--rhi=null',
  109. '--regset="/Amazon/Settings/EnableSourceControl=false"',
  110. '--regset="/Amazon/AWS/Preferences/AWSAttributionConsentShown=true"',
  111. '--regset="/Amazon/AWS/Preferences/AWSAttributionEnabled=false"',
  112. '--skipWelcomeScreenDialog=true',
  113. '+wait_seconds','10',
  114. '+quit'
  115. ]
  116. result = context.run(cmd, timeout=2*60)
  117. assert result.returncode == 0, f"Editor.exe failed with exit code {result.returncode}"
  118. except TimeoutExpired as e:
  119. pass
  120. editor_log = context.project_path / 'user/log/Editor.log'
  121. assert editor_log.is_file(), "Editor.log file not found"
  122. # expect to find "Engine initialized" in Editor log
  123. engine_initialized_message_found = False
  124. with editor_log.open('r') as f:
  125. engine_initialized_message_found = ('Engine initialized' in f.read())
  126. assert engine_initialized_message_found, "Engine initialized message not found in editor.log"
  127. @pytest.fixture(scope="session")
  128. def test_run_launcher_fixture(test_run_asset_processor_batch_fixture, context):
  129. """ Game launcher can be run without crashing. """
  130. project_name = Path(context.project_path).name
  131. launcher_filename = f"{project_name}.GameLauncher.exe"
  132. try:
  133. # run profile launcher for 2 mins
  134. result = context.run([str(context.project_bin_path_profile / launcher_filename),'--rhi=null'], cwd=context.project_bin_path_profile, timeout=2*60)
  135. assert result.returncode == 0, f"{launcher_filename} failed with exit code {result.returncode}"
  136. except TimeoutExpired as e:
  137. # we expect to close the app on timeout ourselves
  138. # deferred console commands are disabled in launcher
  139. pass
  140. game_log = context.project_path / 'user/log/Game.log'
  141. assert game_log.is_file(), "Game.log file not found"
  142. exception_found = False
  143. with game_log.open('r') as f:
  144. exception_found = ('Exception with exit code' in f.read())
  145. assert not exception_found, "Exception found in Game.log"
  146. @pytest.fixture(scope="session")
  147. def test_uninstall_fixture(test_run_launcher_fixture, test_run_editor_fixture, context):
  148. """ Uninstall succeeds and unregisters the engine. """
  149. assert context.installer_path.is_file(), f"Invalid installer path {context.installer_path}"
  150. engine_json_data = manifest.get_engine_json_data(engine_name=None, engine_path=context.install_root)
  151. assert engine_json_data, f"Failed to get engine.json data for engine in {context.install_root}"
  152. assert 'engine_name' in engine_json_data, "Engine.json does not contain the engine_name key"
  153. # when the installer is run with timeout of 30 minutes
  154. result = context.run([str(context.installer_path) , f"InstallFolder={context.install_root}", "/quiet","/uninstall"], timeout=30*60)
  155. # the installer succeeds
  156. assert result.returncode == 0, f"Installer failed with exit code {result.returncode}"
  157. # the engine is no longer registered
  158. engine_name = engine_json_data['engine_name']
  159. engine_registered_path = manifest.get_registered(engine_name = engine_name)
  160. assert not engine_registered_path, f"{engine_name} is still registered with the path {engine_registered_path}"
  161. def test_installer(test_uninstall_fixture):
  162. """ PyTest needs one non-fixture test. Change the 'test_uninstall_fixture' param to the
  163. specific fixture you want to test during development.
  164. """
  165. assert True