2021-09-29 15:50:57 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
using System ;
using System.Collections.Generic ;
using System.IO ;
using System.Linq ;
using System.Xml.Linq ;
using UnrealBuildBase ;
using UnrealBuildTool ;
public class LowLevelTests : ModuleRules
{
2021-11-08 09:18:29 -05:00
private readonly XNamespace BuildGraphNamespace = XNamespace . Get ( "http://www.epicgames.com/BuildGraph" ) ;
private readonly XNamespace SchemaInstance = XNamespace . Get ( "http://www.w3.org/2001/XMLSchema-instance" ) ;
private readonly XNamespace SchemaLocation = XNamespace . Get ( "http://www.epicgames.com/BuildGraph ../../Build/Graph/Schema.xsd" ) ;
2021-09-29 15:50:57 -04:00
2021-11-08 09:18:29 -05:00
public virtual string TestName = > throw new Exception ( "TestName must be overwritten in subclasses." ) ;
public virtual string TestShortName = > throw new Exception ( "TestShortName must be overwritten in subclasses." ) ;
public virtual string ResourcesPath = > string . Empty ;
public virtual bool UsesCatch2 = > true ;
2021-09-29 15:50:57 -04:00
2021-11-11 14:55:09 -05:00
public virtual bool Disabled = > false ;
2021-09-29 15:50:57 -04:00
public LowLevelTests ( ReadOnlyTargetRules Target ) : base ( Target )
{
2021-11-08 09:18:29 -05:00
if ( UsesCatch2 )
2021-09-29 15:50:57 -04:00
{
2021-11-08 09:18:29 -05:00
PCHUsage = PCHUsageMode . NoPCHs ;
PrecompileForTargets = PrecompileTargetsType . None ;
2021-09-29 15:50:57 -04:00
2021-11-08 09:18:29 -05:00
if ( Target . Configuration = = UnrealTargetConfiguration . Debug & & Target . Platform = = UnrealTargetPlatform . Linux )
{
OptimizeCode = CodeOptimization . Never ;
}
2021-09-29 15:50:57 -04:00
2021-11-08 09:18:29 -05:00
bAllowConfidentialPlatformDefines = true ;
bLegalToDistributeObjectCode = true ;
2021-09-29 15:50:57 -04:00
2021-11-08 09:18:29 -05:00
// Required false for catch.hpp
bUseUnity = false ;
2021-09-29 15:50:57 -04:00
2021-11-08 09:18:29 -05:00
// Disable exception handling so that tests can assert for exceptions
bEnableObjCExceptions = false ;
bEnableExceptions = false ;
PrivateDependencyModuleNames . AddRange (
new string [ ] {
2021-10-15 12:17:53 -04:00
"Core" ,
"Projects" ,
2021-09-29 15:50:57 -04:00
"LowLevelTestsRunner"
2021-11-08 09:18:29 -05:00
} ) ;
2021-09-29 15:50:57 -04:00
2021-11-08 09:18:29 -05:00
PrivateIncludePaths . Add ( "Runtime/Launch/Private" ) ;
PrivateIncludePathModuleNames . Add ( "Launch" ) ;
2021-10-15 12:17:53 -04:00
2021-11-08 09:18:29 -05:00
// Platforms specific setup
if ( Target . Platform = = UnrealTargetPlatform . Android )
{
PublicDefinitions . Add ( "CATCH_CONFIG_NOSTDOUT" ) ;
}
}
if ( this . GetType ( ) ! = typeof ( LowLevelTests ) )
2021-09-29 15:50:57 -04:00
{
2021-11-08 09:18:29 -05:00
UpdateBuildGraphPropertiesFile ( ) ;
2021-09-29 15:50:57 -04:00
}
}
/// <summary>
/// Generates or updates include file for LowLevelTests.xml containing test flags: name, short name, target name, relative binaries path, supported platforms etc.
2021-11-08 09:18:29 -05:00
/// <paramref name="TestModule">The test module build class that inherits form LowLevelTests</paramref>
2021-09-29 15:50:57 -04:00
/// </summary>
2021-11-08 09:18:29 -05:00
private void UpdateBuildGraphPropertiesFile ( )
2021-09-29 15:50:57 -04:00
{
2021-11-08 09:18:29 -05:00
bool IsPublic = false ;
string GeneratedPropertiesScriptFile ;
string RestrictedFolder = Path . Combine ( Unreal . EngineDirectory . FullName , "Restricted" ) ;
string NotForLicenseesFolder = Path . Combine ( RestrictedFolder , "NotForLicensees" ) ;
string NonPublicFolder = Path . Combine ( NotForLicenseesFolder , "Build" ) ;
string NonPublicPath = Path . Combine ( NonPublicFolder , "LowLevelTests_GenProps.xml" ) ;
if ( IsRestrictedPath ( ModuleDirectory ) )
2021-09-29 15:50:57 -04:00
{
2021-11-08 09:18:29 -05:00
GeneratedPropertiesScriptFile = NonPublicPath ;
}
else
{
IsPublic = true ;
GeneratedPropertiesScriptFile = Path . Combine ( Unreal . EngineDirectory . FullName , "Build" , "LowLevelTests_GenProps.xml" ) ;
}
// UE-133126
if ( Directory . GetFileSystemEntries ( NonPublicFolder ) . Length = = 1 & & File . Exists ( NonPublicPath ) )
{
File . Delete ( NonPublicPath ) ;
Directory . Delete ( NonPublicFolder ) ;
Directory . Delete ( NotForLicenseesFolder ) ;
Directory . Delete ( RestrictedFolder ) ;
2021-09-29 15:50:57 -04:00
}
if ( ! File . Exists ( GeneratedPropertiesScriptFile ) )
{
Directory . CreateDirectory ( Path . GetDirectoryName ( GeneratedPropertiesScriptFile ) ) ;
using ( FileStream fileStream = File . Create ( GeneratedPropertiesScriptFile ) )
{
2021-11-08 09:18:29 -05:00
XDocument initFile = new XDocument ( new XElement ( BuildGraphNamespace + "BuildGraph" , new XAttribute ( XNamespace . Xmlns + "xsi" , SchemaInstance ) , new XAttribute ( SchemaInstance + "schemaLocation" , SchemaLocation ) ) ) ;
initFile . Root . Add (
new XElement (
BuildGraphNamespace + "Property" ,
new XAttribute ( "Name" , "TestNames" + ( ! IsPublic ? "Restricted" : "" ) ) ,
new XAttribute ( "Value" , string . Empty ) ) ) ;
2021-09-29 15:50:57 -04:00
initFile . Save ( fileStream ) ;
}
}
2021-11-08 09:18:29 -05:00
// All relevant properties
string TestTargetName = Target . LaunchModuleName ;
string TestBinariesPath = TryGetBinariesPath ( ) ;
string TestResourcesPath = ResourcesPath ;
// Do not save full paths
if ( Path . IsPathRooted ( TestBinariesPath ) )
2021-09-29 15:50:57 -04:00
{
2021-11-08 09:18:29 -05:00
TestBinariesPath = Path . GetRelativePath ( Unreal . RootDirectory . FullName , TestBinariesPath ) ;
2021-09-29 15:50:57 -04:00
}
if ( Path . IsPathRooted ( ResourcesPath ) )
{
2021-11-08 09:18:29 -05:00
TestResourcesPath = Path . GetRelativePath ( Unreal . RootDirectory . FullName , ResourcesPath ) ;
2021-09-29 15:50:57 -04:00
}
2021-11-08 09:18:29 -05:00
XDocument GenPropsDoc = XDocument . Load ( GeneratedPropertiesScriptFile ) ;
XElement Root = GenPropsDoc . Root ;
// First descendant must be TestNames
XElement TestNames = ( XElement ) Root . FirstNode ;
string AllTestNames = TestNames . Attribute ( "Value" ) . Value ;
if ( ! AllTestNames . Contains ( TestName ) )
{
if ( string . IsNullOrEmpty ( AllTestNames ) )
{
AllTestNames + = TestName ;
}
else if ( ! AllTestNames . Contains ( TestName ) )
{
AllTestNames + = ";" + TestName ;
}
}
TestNames . Attribute ( "Value" ) . SetValue ( AllTestNames ) ;
XElement lastUpdatedNode = TestNames ;
2021-11-11 14:55:09 -05:00
InsertOrUpdateTestFlag ( ref lastUpdatedNode , TestName , "Disabled" , Disabled . ToString ( ) ) ;
2021-11-08 09:18:29 -05:00
InsertOrUpdateTestFlag ( ref lastUpdatedNode , TestName , "Short" , TestShortName ) ;
InsertOrUpdateTestFlag ( ref lastUpdatedNode , TestName , "Target" , TestTargetName ) ;
InsertOrUpdateTestFlag ( ref lastUpdatedNode , TestName , "BinariesRelative" , TestBinariesPath ) ;
InsertOrUpdateTestFlag ( ref lastUpdatedNode , TestName , "Resources" , TestResourcesPath ) ;
InsertOrUpdateTestFlag ( ref lastUpdatedNode , TestName , "ResourcesDest" , Path . GetFileName ( TestResourcesPath ) ) ;
2021-09-29 15:50:57 -04:00
InsertOrUpdateTestOption ( ref lastUpdatedNode , TestName , TestShortName , "Run" , "Tests" , false . ToString ( ) ) ;
2021-11-08 09:18:29 -05:00
List < UnrealTargetPlatform > AllSupportedPlatforms = new List < UnrealTargetPlatform > ( ) ;
var SupportedPlatforms = GetType ( ) . GetCustomAttributes ( typeof ( SupportedPlatformsAttribute ) , false ) ;
2021-09-29 15:50:57 -04:00
// If none specified we assume all platforms are supported by default
2021-11-08 09:18:29 -05:00
if ( SupportedPlatforms . Length = = 0 )
2021-09-29 15:50:57 -04:00
{
2021-11-08 09:18:29 -05:00
AllSupportedPlatforms . AddRange ( UnrealTargetPlatform . GetValidPlatforms ( ) . ToList ( ) ) ;
2021-09-29 15:50:57 -04:00
}
else
{
2021-11-08 09:18:29 -05:00
foreach ( var Platform in SupportedPlatforms )
2021-09-29 15:50:57 -04:00
{
2021-11-08 09:18:29 -05:00
AllSupportedPlatforms . AddRange ( ( ( SupportedPlatformsAttribute ) Platform ) . Platforms ) ;
2021-09-29 15:50:57 -04:00
}
}
2021-11-08 09:18:29 -05:00
InsertOrUpdateTestFlag ( ref lastUpdatedNode , TestName , "SupportedPlatforms" , AllSupportedPlatforms . Aggregate ( "" , ( current , next ) = > ( current = = "" ? next . ToString ( ) : current + ";" + next . ToString ( ) ) ) ) ;
2021-09-29 15:50:57 -04:00
try
{
2021-11-08 09:18:29 -05:00
GenPropsDoc . Save ( GeneratedPropertiesScriptFile ) ;
2021-09-29 15:50:57 -04:00
}
catch ( UnauthorizedAccessException )
{
// Expected on build machines.
// TODO: Ability to build for generate files and runnable tests.
}
}
2021-11-08 09:18:29 -05:00
private bool IsRestrictedPath ( string ModuleDirectory )
2021-09-29 15:50:57 -04:00
{
2021-11-08 09:18:29 -05:00
foreach ( string RestrictedFolderName in RestrictedFolder . GetNames ( ) )
{
if ( ModuleDirectory . Contains ( RestrictedFolderName ) )
{
return true ;
}
}
return false ;
}
private string TryGetBinariesPath ( )
{
int SourceFolderIndex = ModuleDirectory . IndexOf ( "Source" ) ;
if ( SourceFolderIndex < 0 )
{
throw new Exception ( "Could not detect source folder path for module " + GetType ( ) ) ;
}
return ModuleDirectory . Substring ( 0 , SourceFolderIndex ) + "Binaries" ;
}
private void InsertOrUpdateTestFlag ( ref XElement ElementUpsertAfter , string TestName , string FlagSuffix , string FlagValue )
{
IEnumerable < XElement > NextChunk = ElementUpsertAfter . ElementsAfterSelf ( BuildGraphNamespace + "Property" )
2021-09-29 15:50:57 -04:00
. Where ( prop = > prop . Attribute ( "Name" ) . Value . EndsWith ( FlagSuffix ) ) ;
if ( NextChunk
. Where ( prop = > prop . Attribute ( "Name" ) . Value = = TestName + FlagSuffix )
. Count ( ) = = 0 )
{
2021-11-08 09:18:29 -05:00
XElement ElementInsert = new XElement ( BuildGraphNamespace + "Property" ) ;
2021-09-29 15:50:57 -04:00
ElementInsert . SetAttributeValue ( "Name" , TestName + FlagSuffix ) ;
ElementInsert . SetAttributeValue ( "Value" , FlagValue ) ;
ElementUpsertAfter . AddAfterSelf ( ElementInsert ) ;
2021-11-08 09:18:29 -05:00
}
else
2021-09-29 15:50:57 -04:00
{
NextChunk
. Where ( prop = > prop . Attribute ( "Name" ) . Value = = TestName + FlagSuffix ) . First ( ) . SetAttributeValue ( "Value" , FlagValue ) ;
}
ElementUpsertAfter = NextChunk . Last ( ) ;
}
2021-11-08 09:18:29 -05:00
private void InsertOrUpdateTestOption ( ref XElement ElementUpsertAfter , string TestName , string TestShortName , string OptionPrefix , string OptionSuffix , string DefaultValue )
2021-09-29 15:50:57 -04:00
{
2021-11-08 09:18:29 -05:00
IEnumerable < XElement > NextChunk = ElementUpsertAfter . ElementsAfterSelf ( BuildGraphNamespace + "Option" )
2021-09-29 15:50:57 -04:00
. Where ( prop = > prop . Attribute ( "Name" ) . Value . StartsWith ( OptionPrefix ) & & prop . Attribute ( "Name" ) . Value . EndsWith ( OptionSuffix ) ) ;
if ( NextChunk
. Where ( prop = > prop . Attribute ( "Name" ) . Value = = OptionPrefix + TestName + OptionSuffix )
. Count ( ) = = 0 )
{
2021-11-08 09:18:29 -05:00
XElement ElementInsert = new XElement ( BuildGraphNamespace + "Option" ) ;
2021-09-29 15:50:57 -04:00
ElementInsert . SetAttributeValue ( "Name" , OptionPrefix + TestName + OptionSuffix ) ;
ElementInsert . SetAttributeValue ( "DefaultValue" , DefaultValue ) ;
ElementInsert . SetAttributeValue ( "Description" , string . Format ( "{0} {1} {2}" , OptionPrefix , TestShortName , OptionSuffix ) ) ;
ElementUpsertAfter . AddAfterSelf ( ElementInsert ) ;
2021-11-08 09:18:29 -05:00
}
else
2021-09-29 15:50:57 -04:00
{
XElement ElementUpdate = NextChunk
. Where ( prop = > prop . Attribute ( "Name" ) . Value = = OptionPrefix + TestName + OptionSuffix ) . First ( ) ;
ElementUpdate . SetAttributeValue ( "Description" , string . Format ( "{0} {1} {2}" , OptionPrefix , TestShortName , OptionSuffix ) ) ;
ElementUpdate . SetAttributeValue ( "DefaultValue" , DefaultValue ) ;
}
ElementUpsertAfter = NextChunk . Last ( ) ;
}
}