diff --git a/service/README.md b/service/README.md new file mode 100644 index 0000000..5b89cee --- /dev/null +++ b/service/README.md @@ -0,0 +1,3 @@ +# Service utils and scripts that can be used to assist in the repository development + +* **remove_bom.py** - removes BOM from file (It can be used, for example, to process files with UTF encoding which were saved with powershell version 5 or older) diff --git a/service/remove_bom.py b/service/remove_bom.py new file mode 100644 index 0000000..0d3784b --- /dev/null +++ b/service/remove_bom.py @@ -0,0 +1,27 @@ +# This script removes BOM from the specified file. +# It can be used, for example, to process files with UTF encoding which were saved with powershell version 5 or older +# Usage: python remove_bom.py input_filename + +def remove_bom(input_filename): + # Read the file in binary mode to preserve BOM + with open(input_filename, 'rb') as f: + content = f.read() + + # Check for BOM and remove it + if content.startswith(b'\xef\xbb\xbf'): + content = content[3:] + + # Write the content back to the same file with UTF-8 encoding + with open(input_filename, 'wb') as f: + f.write(content) + +if __name__ == "__main__": + import sys + + if len(sys.argv) != 2: + print("[remove_bom.py]: Wrong usage. Correct usage: python remove_bom.py input_filename") + sys.exit(1) + + input_filename = sys.argv[1] + + remove_bom(input_filename) diff --git a/setup/config.template b/setup/config.template index fb51e93..bb61028 100644 --- a/setup/config.template +++ b/setup/config.template @@ -46,6 +46,8 @@ rem Tests set TestNames= set TestOutputLogPath=%ProjectRoot%\Build\Tests\Tests.log set ReportOutputPath=%ProjectRoot%\Build\Tests -set ExludedPathForTestReport=%SourceCodePath%\%ProjectPureName%\Tests +set RelativePathToTests=Tests +set ExcludedPathForTestReport=%SourceCodePath%\%ProjectPureName%\%RelativePathToTests% set UEAutomationContentPath=%EnginePath%\Engine\Content\Automation set OpenCPPCoveragePath=C:\Program Files\OpenCppCoverage\OpenCppCoverage.exe +set TestsModuleName= \ No newline at end of file diff --git a/tests/README.md b/tests/README.md index 419817c..1543b6e 100644 --- a/tests/README.md +++ b/tests/README.md @@ -8,6 +8,7 @@ ## Test files creation * **create_spec_file.bat** helps to create a spec file for testing * **create_test_file.bat** helps to create a legacy test file +* **assist_tests_module_creation.bat** facilitates the creation of a UE module for testing by creating .Build.cs, .h and .cpp files ## Setup and run * **setup_tests.bat** installs all requirement files to **data** for local test running (**data** folder added in **.gitignore** file of repository) diff --git a/tests/assist_tests_module_creation.bat b/tests/assist_tests_module_creation.bat new file mode 100644 index 0000000..735beda --- /dev/null +++ b/tests/assist_tests_module_creation.bat @@ -0,0 +1,179 @@ +:: Copyright LifeEXE. All Rights Reserved. + +@echo off + +rem This script facilitates the creation of a UE module for your game tests. +rem Additionally, it prompts the user to assign TestsModuleName variable in config.bat, using the name of this module. +rem This variable streamlines the process of creating new test files within the designated module and enables the exclusion of this module from the OpenCppCoverage report. + +set "ConfigBatFullPath=%~dp0..\..\devops_data\config.bat" +call "%ConfigBatFullPath%" + +:begin +set /p NewModuleName= "Enter the name of module for tests you want to create :" +if [%NewModuleName%]==[] goto:begin + +rem .build.cs / .h / .cpp file names +set ModuleBuildFileName=%NewModuleName%.Build.cs +set ModuleCppFileName=%NewModuleName%.cpp +set ModuleHFileName=%NewModuleName%.h + +rem Full paths to .build.cs / .h / .cpp files to create +set ModuleAbsoluteDir=%SourceCodePath%\%NewModuleName% +set ModuleBuildFilePath=%ModuleAbsoluteDir%\%ModuleBuildFileName% +set ModuleCppFilePath=%ModuleAbsoluteDir%\%ModuleCppFileName% +set ModuleHFilePath=%ModuleAbsoluteDir%\%ModuleHFileName% + +rem Confirmation info +echo. +echo =========== Files to be created: =========== +echo %ModuleBuildFilePath% +echo %ModuleCppFilePath% +echo %ModuleHFilePath% +if exist "%ModuleAbsoluteDir%" ( + echo. + echo Directory "%ModuleAbsoluteDir%" already exists. If it contains the files listed above, they will be overwritten. +) + +:: Find out if we need to add TestsModuleName to config.bat +set bStringFound=FALSE +set "SetTestsModuleNameString=set TestsModuleName=" +call :findStringInFile "%SetTestsModuleNameString%" , "%ConfigBatFullPath%" +set TestsModuleNameExisitsInConfig=FALSE +if "%bStringFound%"=="TRUE" ( + set TestsModuleNameExisitsInConfig=TRUE +) + +:: Find out if TestsModuleName exists in config.bat and if we need to define or re-define it +set TestsModuleNameToBeChanged=FALSE +if "%TestsModuleNameExisitsInConfig%"=="TRUE" if [%TestsModuleName%]==[] goto :setTestsModuleNameToBeChanged +if "%TestsModuleNameExisitsInConfig%"=="TRUE" if not "%TestsModuleName%"=="%NewModuleName%" goto :setTestsModuleNameToBeChanged +goto :afterSetTestsModuleNameToBeChanged +:setTestsModuleNameToBeChanged +set TestsModuleNameToBeChanged=TRUE +:afterSetTestsModuleNameToBeChanged + +:: Don't print TestsModuleName info block if it's empty +if "%TestsModuleNameExisitsInConfig%"=="TRUE" if "%TestsModuleNameToBeChanged%"=="FALSE" goto :endOfConfirmationInfo + +echo =========== TestsModuleName: =========== +if "%TestsModuleNameToBeChanged%"=="TRUE" ( + echo TestsModuleName is currently defined in config.bat as %TestsModuleName%. + echo It will be re-defined as %NewModuleName%. +) + +if "%TestsModuleNameExisitsInConfig%"=="FALSE" ( + echo TestsModuleName is not found in config.bat. It will be added and defined as %NewModuleName%. +) + +:endOfConfirmationInfo +echo ====================================== +echo. + +set /p UserConfirmed= "Confirm? [Y/N or (E)xit] :" +if /i "%UserConfirmed%"=="N" goto :begin +if /i "%UserConfirmed%"=="E" goto :EOF + +rem Create dir for the module +if not exist "%ModuleAbsoluteDir%" mkdir "%ModuleAbsoluteDir%" + +rem Full paths .build.cs / .h / .cpp template files +set ModuleBuildTemplateFilePath=%ProjectRoot%\devops_ue\tests\templates\tests_module\TestsModule.Build.cs.template +set ModuleCppTemplateFilePath=%ProjectRoot%\devops_ue\tests\templates\tests_module\TestsModule.cpp.template +set ModuleHTemplateFilePath=%ProjectRoot%\devops_ue\tests\templates\tests_module\TestsModule.h.template + +rem Remove old module files if exist +set TmpEchoLine=Deleting module files if exist +echo %TmpEchoLine%... +del /q "%ModuleBuildFilePath%" +del /q "%ModuleCppFilePath%" +del /q "%ModuleHFilePath%" +echo %TmpEchoLine%: Complete + +rem create actual files +set TmpEchoLine=Creating module files from templates +echo %TmpEchoLine%... +call :createTemplate "%ModuleBuildTemplateFilePath%" , "%ModuleBuildFilePath%" +call :createTemplate "%ModuleCppTemplateFilePath%" , "%ModuleCppFilePath%" +call :createTemplate "%ModuleHTemplateFilePath%" , "%ModuleHFilePath%" +echo %TmpEchoLine%: Complete + +rem Dealing with TestsModuleName in config.bat +set TmpEchoLine=Setting TestsModuleName in config.bat +echo %TmpEchoLine%... +:: Add TestsModuleName (with undefined value) to config.bat +if "%TestsModuleNameExisitsInConfig%"=="FALSE" ( + echo. >> "%ConfigBatFullPath%" + echo. >> "%ConfigBatFullPath%" + echo %SetTestsModuleNameString% >> "%ConfigBatFullPath%" +) +:: Define or re-define TestsModuleName in config.bat +set before=%SetTestsModuleNameString% +set after=%SetTestsModuleNameString%%NewModuleName% +:: If you need config.bat to be in UTF8, then there is problem with BOM. +:: The problem is that PowerShell versions up to 5.1 including PowerShell Core 6.x (used by default in Windows 10 versions up to 1909) save files in UTF8 adding BOM. Becuase of this we need to delete BOM, otherwise files saved by powerwhell become binary. And config.bat should not contain BOM to be read without erorrs. +:: However, if you don't need config.bat to be saved in UTF8, it can be saved in ANSCII. To do it, comment out or remove "UTF8 version" block, and uncomment the ASCII version below (it works for all versions of powershell). +:: ASCII version of the powershell comand below works fine: +:: powershell -Command "(gc '%ConfigBatFullPath%') -replace '^%before%.*', '%after%' | Out-File -encoding ASCII '%ConfigBatFullPath%'" +:: UTF8 version +powershell -Command "(gc '%ConfigBatFullPath%') -replace '^%before%.*', '%after%' | Out-File -encoding UTF8 '%ConfigBatFullPath%'" +call :normalizePath "%ConfigBatFullPath%" +set ConfigBatNormalizedFullPath=%RETVAL% +call :removeBOM "%ConfigBatNormalizedFullPath%" +:: end of UTF8 version +echo %TmpEchoLine%: Complete + +rem Clang-format +set TmpEchoLine=Clang format +echo %TmpEchoLine%... +call "%~dp0..\misc\format_all_files.bat" +echo %TmpEchoLine%: Complete + +goto :EOF + +:: ========== FUNCTIONS ========== + +rem Function to create .build.cs / .h / .cpp from template +:createTemplate +set TemplateName=%~1 +set FileToWriteIn=%~2 +for /f "usebackq tokens=*" %%a in ("%TemplateName%") do ( + if %%a == NEW_LINE ( + echo.>>"%FileToWriteIn%" + ) else ( + call echo %%a>>"%FileToWriteIn%" + ) +) +exit /b + +rem Function to find out if a line exists in a a file. Sets bStringFound=TRUE if found +:findStringInFile +set bStringFound=FALSE +set InSearchString=%~1 +set InFilePath=%~2 +rem Loop through each line of the file +for /f "usebackq tokens=*" %%a in ("%InFilePath%") do ( + rem Check if the line starts with the specified string + echo %%a | findstr /b /c:"%InSearchString%" >nul + if not errorlevel 1 ( + set bStringFound=TRUE + ) +) +exit /b + +rem Call the Python script to remove BOM +:removeBOM +python ..\service\remove_bom.py "%~1" +:: Check if Python script execution was successful +if %errorlevel% neq 0 ( + echo ERROR in [remove_bom.py]: Error occurred during conversion. You may need to convert the config.bat file manually into UTF8. + pause +) else ( + echo [remove_bom.py]: Conversion completed successfully. +) +exit /b + +rem Path normalization: e.g. it can turn path representation from "dir1\dir2\..\dir3" into "dir1\dir3" +:normalizePath +set RETVAL=%~f1 +exit /b diff --git a/tests/create_spec_file.bat b/tests/create_spec_file.bat index 69c9a1f..b201c78 100644 --- a/tests/create_spec_file.bat +++ b/tests/create_spec_file.bat @@ -5,28 +5,38 @@ call "%~dp0..\..\devops_data\config.bat" :begin set /p TestClassName= "Enter test class name (without word 'Test' in the name) :" -if [%TestClassName%]==[] goto:begin -set /p TestRelativePath= "Enter relative to [Source\%ProjectPureName%] directory (use \ symbol for subdirs):" +if [%TestClassName%]==[] goto :begin + +rem Setting TestRelativePath, ModuleName +call set_TestRelativePath.bat +if "%RETURNED_VALUE%"=="EXIT" goto :EOF rem '.spec.cpp' file name set TestCppFileName=%TestClassName%.spec.cpp rem full path to '.spec.cpp' file to create -set TestAbsoluteDir=%SourceCodePath%\%ProjectPureName%\%TestRelativePath% -if [%TestRelativePath%]==[] set TestAbsoluteDir=%SourceCodePath%\%ProjectPureName% +set TestAbsoluteDir=%SourceCodePath%\%ModuleName%\%TestRelativePath% +if [%TestRelativePath%]==[] set TestAbsoluteDir=%SourceCodePath%\%ModuleName% set TestCppFilePath=%TestAbsoluteDir%\%TestCppFileName% rem Confirmation echo. echo =========== File to be created: =========== echo %TestCppFilePath% +echo. +echo =========== FYI: Path to be excluded from OpenCppCoverage report: =========== +echo %ExcludedPathForTestReport%* +if not [%TestsModuleName%]==[] ( + echo %SourceCodePath%\%TestsModuleName%* +) +echo. echo ====================================== echo. set /p UserConfirmed= "Confirm? [Y/N or (E)xit] :" -if %UserConfirmed% == N goto:begin -if %UserConfirmed% == n goto:begin -if %UserConfirmed% == E goto:EOF -if %UserConfirmed% == e goto:EOF +if %UserConfirmed% == N goto :begin +if %UserConfirmed% == n goto :begin +if %UserConfirmed% == E goto :EOF +if %UserConfirmed% == e goto :EOF rem create dir if not exist "%TestAbsoluteDir%" mkdir "%TestAbsoluteDir%" @@ -47,8 +57,12 @@ call :createTemplate "%TestCppTemplateFilePath%" , "%TestCppFilePath%" rem clang-format call "%~dp0..\misc\format_all_files.bat" -echo %TEST_INCLUDE_FILE_1% -goto:EOF +:: debug +:: echo %TEST_INCLUDE_FILE% + +goto :EOF + +:: ========== FUNCTIONS ========== rem function to create .h / .cpp from template :createTemplate @@ -61,4 +75,4 @@ for /f "usebackq tokens=*" %%a in ("%TemplateName%") do ( call echo %%a>>"%FileToWriteIn%" ) ) - +exit /b diff --git a/tests/create_test_file.bat b/tests/create_test_file.bat index c750dc5..1dd5bdd 100644 --- a/tests/create_test_file.bat +++ b/tests/create_test_file.bat @@ -5,16 +5,19 @@ call "%~dp0..\..\devops_data\config.bat" :begin set /p TestClassName= "Enter test class name :" -if [%TestClassName%]==[] goto:begin -set /p TestRelativePath= "Enter relative to [Source\%ProjectPureName%] directory (use \ symbol for subdirs):" +if [%TestClassName%]==[] goto :begin + +rem Setting TestRelativePath, ModuleName +call set_TestRelativePath.bat +if "%RETURNED_VALUE%"=="EXIT" goto :EOF rem .h / .cpp file names set TestCppFileName=%TestClassName%.cpp set TestHFileName=%TestClassName%.h rem full paths to .h / .cpp files to create -set TestAbsoluteDir=%SourceCodePath%\%ProjectPureName%\%TestRelativePath% -if [%TestRelativePath%]==[] set TestAbsoluteDir=%SourceCodePath%\%ProjectPureName% +set TestAbsoluteDir=%SourceCodePath%\%ModuleName%\%TestRelativePath% +if [%TestRelativePath%]==[] set TestAbsoluteDir=%SourceCodePath%\%ModuleName% set TestCppFilePath=%TestAbsoluteDir%\%TestCppFileName% set TestHFilePath=%TestAbsoluteDir%\%TestHFileName% @@ -23,13 +26,20 @@ echo. echo =========== Files to be created: =========== echo %TestCppFilePath% echo %TestHFilePath% +echo. +echo =========== FYI: Path to be excluded from OpenCppCoverage report: =========== +echo %ExcludedPathForTestReport%* +if not [%TestsModuleName%]==[] ( + echo %SourceCodePath%\%TestsModuleName%* +) +echo. echo ====================================== echo. set /p UserConfirmed= "Confirm? [Y/N or (E)xit] :" -if %UserConfirmed% == N goto:begin -if %UserConfirmed% == n goto:begin -if %UserConfirmed% == E goto:EOF -if %UserConfirmed% == e goto:EOF +if %UserConfirmed% == N goto :begin +if %UserConfirmed% == n goto :begin +if %UserConfirmed% == E goto :EOF +if %UserConfirmed% == e goto :EOF rem create dir if not exist "%TestAbsoluteDir%" mkdir "%TestAbsoluteDir%" @@ -40,7 +50,7 @@ set TestHTemplateFilePath=%ProjectRoot%\devops_ue\tests\templates\Test.h.templat rem template file vars rem path with \ -set TempPath=%ProjectPureName%\%TestRelativePath%\%TestClassName%.h +set TempPath=%ModuleName%\%TestRelativePath%\%TestClassName%.h rem replace \ with / for include string set TEST_INCLUDE_FILE="%TempPath:\=/%" set "OR=^|" @@ -57,8 +67,12 @@ call :createTemplate "%TestHTemplateFilePath%" , "%TestHFilePath%" rem clang-format call "%~dp0..\misc\format_all_files.bat" -echo %TEST_INCLUDE_FILE_1% -goto:EOF +:: debug +:: echo %TEST_INCLUDE_FILE% + +:: ========== FUNCTIONS ========== + +goto :EOF rem function to create .h / .cpp from template :createTemplate @@ -71,4 +85,4 @@ for /f "usebackq tokens=*" %%a in ("%TemplateName%") do ( call echo %%a>>"%FileToWriteIn%" ) ) - +exit /b diff --git a/tests/run_tests.bat b/tests/run_tests.bat index 2b3db90..fced481 100644 --- a/tests/run_tests.bat +++ b/tests/run_tests.bat @@ -30,11 +30,24 @@ set Module=%RETVAL% call :NORMALIZEPATH "%SourceCodePath%" set Sources=%RETVAL% -call :NORMALIZEPATH "%ExludedPathForTestReport%" -set ExludedSources=%RETVAL% +call :NORMALIZEPATH "%ExcludedPathForTestReport%" +set ExcludedSources=%RETVAL% -"%OpenCPPCoveragePath%" --modules="%Module%" --sources="%Sources%" ^ ---excluded_sources="%ExludedSources%" --export_type="%ExportType%" -v -- %TestRunner% +rem Make command to run OpenCppCoverage + +set RunOpenCppCoverageCommand="%OpenCPPCoveragePath%" --modules="%Module%" --sources="%Sources%" + +:: Exclude module with tests from cpp coverage report if the module is defined in config.bat +if [%TestsModuleName%]==[] goto :afterAddingTestsModule +set "TestsModulePath=%Sources%\%TestsModuleName%" +:: excluded_sources argument can be used multiple times +set RunOpenCppCoverageCommand=%RunOpenCppCoverageCommand% --excluded_sources="%TestsModulePath%" +:afterAddingTestsModule + +set RunOpenCppCoverageCommand=%RunOpenCppCoverageCommand% --excluded_sources="%ExcludedSources%" --export_type="%ExportType%" -v -- %TestRunner% + +:: Run OpenCppCoverage +%RunOpenCppCoverageCommand% rem clean obsolete artifacts del /q LastCoverageResults.log diff --git a/tests/set_TestRelativePath.bat b/tests/set_TestRelativePath.bat new file mode 100644 index 0000000..38375dc --- /dev/null +++ b/tests/set_TestRelativePath.bat @@ -0,0 +1,59 @@ +:: Copyright LifeEXE. All Rights Reserved. + +@echo off + +rem This script is designed to be called from create_spec_file.bat and create_test_file.bat +rem It sets TestRelativePath (relative path for saving the test files) and ModuleName (equals to ProjectPureName or TestsModuleName depending on where test files are created) + +set ModuleName=%ProjectPureName% +set UseTestsModule=FALSE + +if [%TestsModuleName%]==[] goto :endOfTestsModule + +:askWhatModule + +rem Suggest creating tests files in the tests module if it is defined +:: ModuleName can be ProjectPureName or TestsModuleName +set RETURNED_VALUE= +echo. +echo TestsModuleName is defined in config.bat: [%TestsModuleName%]. +set /p "UserChoice=Where do you want to create test file(s)? [M - in the tests (M)odule, P - inside your UE game (P)roject, E - Exit] :" +if /i "%UserChoice%"=="M" ( + :: Creating test files in the Tests Module + set ModuleName=%TestsModuleName% + set UseTestsModule=TRUE +) else if /i "%UserChoice%"=="P" ( + rem Just continue with defaults +) else if /i "%UserChoice%"=="E" ( + set RETURNED_VALUE=EXIT + goto :EOF +) else ( + echo Invalid UserChoice. Please enter M, P or E. + goto :askWhatModule +) +:endOfTestsModule + +rem Create test files +set TestRelativePath=%RelativePathToTests% +echo. +echo Defining the directory where you want to create the test file(s)... +echo The target directory relative to [Source\%ModuleName%] is currently "%TestRelativePath%". +echo Please note that OpenCppCoverage will exclude tests from the following path(s): +echo - [%ExcludedPathForTestReport%*] +if /i "%UseTestsModule%"=="TRUE" ( + echo - [%SourceCodePath%\%TestsModuleName%*] +) +:change_TestRelativePath +set RETURNED_VALUE= +set /p "ChangeDirChoice=Do you want to change the target directory? [Y/N or (E)xit] :" +if /i "%ChangeDirChoice%"=="Y" ( + set /p "TestRelativePath=Enter the new relative to [Source\%ModuleName%] directory (use \ symbol for subdirs) :" +) else if /i "%ChangeDirChoice%"=="E" ( + set RETURNED_VALUE=EXIT + goto :EOF +) else if /i "%ChangeDirChoice%"=="N" ( + rem Just continue with the current TestRelativePath +) else ( + echo Invalid ChangeDirChoice. Please enter Y, N or E. + goto :change_TestRelativePath +) \ No newline at end of file diff --git a/tests/templates/tests_module/TestsModule.Build.cs.template b/tests/templates/tests_module/TestsModule.Build.cs.template new file mode 100644 index 0000000..75db34c --- /dev/null +++ b/tests/templates/tests_module/TestsModule.Build.cs.template @@ -0,0 +1,30 @@ +%COPYRIGHT_LINE% +NEW_LINE +using UnrealBuildTool; +NEW_LINE +public class %NewModuleName% : ModuleRules +{ + public %NewModuleName%(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; +NEW_LINE + PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "%ProjectPureName%" }); +NEW_LINE + PublicDependencyModuleNames.AddRange(new string[] { "Json", "JsonUtilities", "UMG" }); +NEW_LINE + if (Target.Configuration != UnrealTargetConfiguration.Shipping) + { + PublicDependencyModuleNames.Add("FunctionalTesting"); + } +NEW_LINE + PrivateDependencyModuleNames.AddRange(new string[] { }); +NEW_LINE + // Uncomment if you are using Slate UI + // PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" }); +NEW_LINE + // Uncomment if you are using online features + // PrivateDependencyModuleNames.Add("OnlineSubsystem"); +NEW_LINE + // To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true + } +} diff --git a/tests/templates/tests_module/TestsModule.cpp.template b/tests/templates/tests_module/TestsModule.cpp.template new file mode 100644 index 0000000..faec516 --- /dev/null +++ b/tests/templates/tests_module/TestsModule.cpp.template @@ -0,0 +1,6 @@ +%COPYRIGHT_LINE% +NEW_LINE +#include "%ModuleHFileName%" +#include "Modules/ModuleManager.h" +NEW_LINE +IMPLEMENT_MODULE(FDefaultGameModuleImpl, %NewModuleName%); diff --git a/tests/templates/tests_module/TestsModule.h.template b/tests/templates/tests_module/TestsModule.h.template new file mode 100644 index 0000000..031cdac --- /dev/null +++ b/tests/templates/tests_module/TestsModule.h.template @@ -0,0 +1,5 @@ +%COPYRIGHT_LINE% +NEW_LINE +#pragma once +NEW_LINE +#include "CoreMinimal.h"