// Copyright Epic Games, Inc. All Rights Reserved.
using AutomatedPerfTesting;
using Gauntlet;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Reflection;
///
/// This file defines the Test Bridge interface. In this context, a "Test Bridge"
/// refers to the custom test session configuration and logic which needs to run
/// before Gauntlet launches the test session on the target device.
///
/// A Test Bridge may contain various attributes which enable composing and defining
/// test-specific config and logic depending on the attribute parameters. These
/// bridges are then instantiated for a given test if the test context parameters match
/// the attribute parameters.
///
/// Simple example would be defining Test Bridges for each Test Type in your project
/// i.e. SequenceBridge, ReplayBridge, ProfileGoBridge etc. such that each bridge
/// is only activated for their corresponding test type.
///
namespace AutomatedPerfTest
{
///
/// Interface to be used if a project wants to configure the Automated Perf Test
/// context, roles and/or config without making intrusive changes to the main
/// node before launching the test.
///
/// Ideally project/game specific command line parameters get set here.
///
public interface ITestBridge
{
///
/// Initial configure call. This is invoked first.
///
/// Test Context
/// APT Config Base
void Configure(UnrealTestContext Context, AutomatedPerfTestConfigBase Config) { }
///
/// Configure client role
///
/// Client Test Role
/// Test Context
/// APT Config Base
void ConfigureClient(UnrealTestRole Role, UnrealTestContext Context, AutomatedPerfTestConfigBase Config) { }
///
/// Configure server role. This is invoked optionally if a server role is required for the test.
///
/// Server Test Role
/// Test Context
/// APT Config Base
void ConfigureServer(UnrealTestRole Role, UnrealTestContext Context, AutomatedPerfTestConfigBase Config) { }
///
/// If this is true, server role is instantiated and configured.
///
///
bool IsServerRoleRequired() { return false; }
}
///
/// Null Test Bridge
///
public class NullBridge : ITestBridge { }
///
/// Static helper class to activate and invoke test bridge configuration
/// functions.
///
public static class AutoTestBridge
{
private static HashSet Bridges = new HashSet();
///
/// Initialize Test Bridge instances with given list of Bridge class names.
///
/// Bridge names
public static void Initialize(IEnumerable InConfigBridges)
{
foreach (string BridgeName in InConfigBridges)
{
ITestBridge ConfigBridge = GetConfigBridge(BridgeName.Trim());
AddBridge(ConfigBridge);
}
}
public static void AddBridge(ITestBridge ConfigBridge)
{
if(ConfigBridge != null)
{
Bridges.Add(ConfigBridge);
Log.Info($"Test Bridge Added: {ConfigBridge.GetType().Name}");
}
}
private static ITestBridge GetConfigBridge(string ConfigBridgeName)
{
if (!string.IsNullOrEmpty(ConfigBridgeName))
{
Type BridgeType = Util.GetTypeWithInterface(ConfigBridgeName);
if (BridgeType != null)
{
return Activator.CreateInstance(BridgeType) as ITestBridge;
}
}
return null;
}
public static void Configure(UnrealTestContext Context, AutomatedPerfTestConfigBase Config)
{
Bridges.ToList().ForEach(Bridge => Bridge?.Configure(Context, Config));
}
public static void ConfigureClient(UnrealTestRole Role, UnrealTestContext Context, AutomatedPerfTestConfigBase Config)
{
Bridges.ToList().ForEach(Bridge => Bridge?.ConfigureClient(Role, Context, Config));
}
public static void ConfigureServer(UnrealTestRole Role, UnrealTestContext Context, AutomatedPerfTestConfigBase Config)
{
Bridges.ToList().ForEach(Bridge => Bridge?.ConfigureServer(Role, Context, Config));
}
public static bool IsServerRoleRequired()
{
return Bridges.Where(B => B.IsServerRoleRequired()).Any();
}
private static bool ValidateAttributes(Type TestBridge, string ProjectName = "")
{
EnableForProjectAttribute ProjectAttribute = TestBridge.GetCustomAttribute();
EnableForParamsAttribute Params = TestBridge.GetCustomAttribute();
AutoBridge BridgeAttribute = TestBridge.GetCustomAttribute();
IEnumerable AutoBridgeTestAttribute = TestBridge.GetCustomAttributes(typeof(AutoBridgeForTestAttribute<>));
bool Result = true;
if (ProjectAttribute != null)
{
Result = ProjectAttribute.ProjectName == ProjectName;
}
if (Params != null)
{
foreach (KeyValuePair Args in Params.Params)
{
string Value = Globals.Params.ParseValue(Args.Key, "");
bool bContainsKey = Globals.Params.ParseParam(Args.Key);
// Some params may be bool params and may only have the param key to
// indicate if it should be activated.
bool bIsBoolVal = string.IsNullOrEmpty(Value) && string.IsNullOrEmpty(Args.Value) && bContainsKey;
bool bIsEqual = string.Equals(Value, Args.Value, StringComparison.OrdinalIgnoreCase);
if (!bIsBoolVal && !bIsEqual)
{
Result = false;
break;
}
}
}
return Result && (BridgeAttribute != null || AutoBridgeTestAttribute.Any());
}
///
/// Registers valid Test Bridge types which will then be used to configure the test
/// depending on the need of the test and/or project.
/// Following attributes used for validation:
///
/// [AutoBridge] - This enables the bridge for all automated perf tests.
/// [AutoBridgeForTest[]] - This enables the bridge only
/// for the specified test type. Multiple types are supported.
/// [EnableForProject] - Ensures the bridge is only enabled for a given project.
/// [EnableForParams]
///
///
/// Type of test being run in this given session
/// Name of project. This should match the name in
/// EnableForProjectAttribute to take effect.
public static void ActivateTestBridges(Type CurrentTestType, string ProjectName = "")
{
Type[] TestBridgeTypes = Util.GetTypesWithInterface();
foreach(Type TestBridge in TestBridgeTypes)
{
AutoBridge BridgeAttribute = TestBridge.GetCustomAttribute();
IEnumerable AutoBridgeTestAttribute = TestBridge.GetCustomAttributes(typeof(AutoBridgeForTestAttribute<>));
// A bridge is valid if each present attribute is valid. If an attribute is null, we do not consider
// it for validation.
if (!ValidateAttributes(TestBridge, ProjectName))
{
continue;
}
if (BridgeAttribute != null)
{
// If a Test Bridge is enabled for all test types, we simply add the bridge
// and continue to the next bridge derived type.
AddBridge(Activator.CreateInstance(TestBridge) as ITestBridge);
continue;
}
// Register bridges which have indicated that they are applicable for a given type of
// test.
foreach (Attribute AutoBridge in AutoBridgeTestAttribute)
{
if (AutoBridge != null && AutoBridge.GetType().IsGenericType)
{
Type CandidateTestType = AutoBridge.GetType().GetGenericArguments().First();
if(CandidateTestType != null && CurrentTestType == CandidateTestType)
{
AddBridge(Activator.CreateInstance(TestBridge) as ITestBridge);
}
}
}
}
}
}
///
/// Automatically activate given Test Bridge for all Automated Perf Tests.
/// If this is the only attribute present, it will be added to all
/// tests by default.
/// Example: [AutoBridge]
///
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class AutoBridge : Attribute
{
public AutoBridge() { }
}
///
/// Automatically activate given Test Bridge for given Automated Perf Test
/// type. If this is the only attribute present, it will be added to
/// all instances of a given test type.
/// Example: [AutoBridgeForTest[ReplayTest]]
///
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class AutoBridgeForTestAttribute : Attribute
where T : IAutomatedPerfTest
{
public Type TestType { get; private set; }
public AutoBridgeForTestAttribute()
{
TestType = typeof(T);
}
}
///
/// Ensures test bridge is activated only for a given project. This ensures if
/// a given bridge is very specific and/or does not apply to any other project
/// it can only be activated for that project.
/// This should be combined with one of the AutoBridge attribute types to ensure
/// auto-activation of the bridge.
/// Example: [EnableForProject("MyProjectName")]
///
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class EnableForProjectAttribute : Attribute
{
public string ProjectName { get; private set; }
public EnableForProjectAttribute(string InProjectName)
{
ProjectName = InProjectName;
}
}
///
/// If a test bridge only applies for a very specific set of parameters,
/// they can be specified here.
/// Example: [EnableForParams("Mode=Test", "SomeBoolVal", ...)]
///
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class EnableForParamsAttribute : Attribute
{
public List> Params { get; private set; }
public EnableForParamsAttribute(params string[] InParams)
{
Params = InParams
.Select(P => P.Split('='))
.Select(S => new KeyValuePair(S[0], S.Length > 1 ? S[1] : null))
.ToList();
}
}
}