You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
380 lines
13 KiB
380 lines
13 KiB
9 years ago
|
using System;
|
||
|
using System.Collections;
|
||
|
using System.Collections.Generic;
|
||
|
using System.Diagnostics;
|
||
|
using System.Linq;
|
||
|
using UnityEngine;
|
||
|
using Debug = UnityEngine.Debug;
|
||
|
using Object = UnityEngine.Object;
|
||
|
|
||
|
namespace UnityTest
|
||
|
{
|
||
|
[Serializable]
|
||
|
public class AssertionComponent : MonoBehaviour, IAssertionComponentConfigurator
|
||
|
{
|
||
|
[SerializeField] public float checkAfterTime = 1f;
|
||
|
[SerializeField] public bool repeatCheckTime = true;
|
||
|
[SerializeField] public float repeatEveryTime = 1f;
|
||
|
[SerializeField] public int checkAfterFrames = 1;
|
||
|
[SerializeField] public bool repeatCheckFrame = true;
|
||
|
[SerializeField] public int repeatEveryFrame = 1;
|
||
|
[SerializeField] public bool hasFailed;
|
||
|
|
||
|
[SerializeField] public CheckMethod checkMethods = CheckMethod.Start;
|
||
|
[SerializeField] private ActionBase m_ActionBase;
|
||
|
|
||
|
[SerializeField] public int checksPerformed = 0;
|
||
|
|
||
|
private int m_CheckOnFrame;
|
||
|
|
||
|
private string m_CreatedInFilePath = "";
|
||
|
private int m_CreatedInFileLine = -1;
|
||
|
|
||
|
public ActionBase Action
|
||
|
{
|
||
|
get { return m_ActionBase; }
|
||
|
set
|
||
|
{
|
||
|
m_ActionBase = value;
|
||
|
m_ActionBase.go = gameObject;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public Object GetFailureReferenceObject()
|
||
|
{
|
||
|
#if UNITY_EDITOR
|
||
|
if (!string.IsNullOrEmpty(m_CreatedInFilePath))
|
||
|
{
|
||
|
return UnityEditor.AssetDatabase.LoadAssetAtPath(m_CreatedInFilePath, typeof(Object));
|
||
|
}
|
||
|
#endif
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
public string GetCreationLocation()
|
||
|
{
|
||
|
if (!string.IsNullOrEmpty(m_CreatedInFilePath))
|
||
|
{
|
||
|
var idx = m_CreatedInFilePath.LastIndexOf("\\") + 1;
|
||
|
return string.Format("{0}, line {1} ({2})", m_CreatedInFilePath.Substring(idx), m_CreatedInFileLine, m_CreatedInFilePath);
|
||
|
}
|
||
|
return "";
|
||
|
}
|
||
|
|
||
|
public void Awake()
|
||
|
{
|
||
|
if (!Debug.isDebugBuild)
|
||
|
Destroy(this);
|
||
|
OnComponentCopy();
|
||
|
}
|
||
|
|
||
|
public void OnValidate()
|
||
|
{
|
||
|
if (Application.isEditor)
|
||
|
OnComponentCopy();
|
||
|
}
|
||
|
|
||
|
private void OnComponentCopy()
|
||
|
{
|
||
|
if (m_ActionBase == null) return;
|
||
|
var oldActionList = Resources.FindObjectsOfTypeAll(typeof(AssertionComponent)).Where(o => ((AssertionComponent)o).m_ActionBase == m_ActionBase && o != this);
|
||
|
|
||
|
// if it's not a copy but a new component don't do anything
|
||
|
if (!oldActionList.Any()) return;
|
||
|
if (oldActionList.Count() > 1)
|
||
|
Debug.LogWarning("More than one refence to comparer found. This shouldn't happen");
|
||
|
|
||
|
var oldAction = oldActionList.First() as AssertionComponent;
|
||
|
m_ActionBase = oldAction.m_ActionBase.CreateCopy(oldAction.gameObject, gameObject);
|
||
|
}
|
||
|
|
||
|
public void Start()
|
||
|
{
|
||
|
CheckAssertionFor(CheckMethod.Start);
|
||
|
|
||
|
if (IsCheckMethodSelected(CheckMethod.AfterPeriodOfTime))
|
||
|
{
|
||
|
StartCoroutine("CheckPeriodically");
|
||
|
}
|
||
|
if (IsCheckMethodSelected(CheckMethod.Update))
|
||
|
{
|
||
|
m_CheckOnFrame = Time.frameCount + checkAfterFrames;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public IEnumerator CheckPeriodically()
|
||
|
{
|
||
|
yield return new WaitForSeconds(checkAfterTime);
|
||
|
CheckAssertionFor(CheckMethod.AfterPeriodOfTime);
|
||
|
while (repeatCheckTime)
|
||
|
{
|
||
|
yield return new WaitForSeconds(repeatEveryTime);
|
||
|
CheckAssertionFor(CheckMethod.AfterPeriodOfTime);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public bool ShouldCheckOnFrame()
|
||
|
{
|
||
|
if (Time.frameCount > m_CheckOnFrame)
|
||
|
{
|
||
|
if (repeatCheckFrame)
|
||
|
m_CheckOnFrame += repeatEveryFrame;
|
||
|
else
|
||
|
m_CheckOnFrame = Int32.MaxValue;
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
public void OnDisable()
|
||
|
{
|
||
|
CheckAssertionFor(CheckMethod.OnDisable);
|
||
|
}
|
||
|
|
||
|
public void OnEnable()
|
||
|
{
|
||
|
CheckAssertionFor(CheckMethod.OnEnable);
|
||
|
}
|
||
|
|
||
|
public void OnDestroy()
|
||
|
{
|
||
|
CheckAssertionFor(CheckMethod.OnDestroy);
|
||
|
}
|
||
|
|
||
|
public void Update()
|
||
|
{
|
||
|
if (IsCheckMethodSelected(CheckMethod.Update) && ShouldCheckOnFrame())
|
||
|
{
|
||
|
CheckAssertionFor(CheckMethod.Update);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void FixedUpdate()
|
||
|
{
|
||
|
CheckAssertionFor(CheckMethod.FixedUpdate);
|
||
|
}
|
||
|
|
||
|
public void LateUpdate()
|
||
|
{
|
||
|
CheckAssertionFor(CheckMethod.LateUpdate);
|
||
|
}
|
||
|
|
||
|
public void OnControllerColliderHit()
|
||
|
{
|
||
|
CheckAssertionFor(CheckMethod.OnControllerColliderHit);
|
||
|
}
|
||
|
|
||
|
public void OnParticleCollision()
|
||
|
{
|
||
|
CheckAssertionFor(CheckMethod.OnParticleCollision);
|
||
|
}
|
||
|
|
||
|
public void OnJointBreak()
|
||
|
{
|
||
|
CheckAssertionFor(CheckMethod.OnJointBreak);
|
||
|
}
|
||
|
|
||
|
public void OnBecameInvisible()
|
||
|
{
|
||
|
CheckAssertionFor(CheckMethod.OnBecameInvisible);
|
||
|
}
|
||
|
|
||
|
public void OnBecameVisible()
|
||
|
{
|
||
|
CheckAssertionFor(CheckMethod.OnBecameVisible);
|
||
|
}
|
||
|
|
||
|
public void OnTriggerEnter()
|
||
|
{
|
||
|
CheckAssertionFor(CheckMethod.OnTriggerEnter);
|
||
|
}
|
||
|
|
||
|
public void OnTriggerExit()
|
||
|
{
|
||
|
CheckAssertionFor(CheckMethod.OnTriggerExit);
|
||
|
}
|
||
|
|
||
|
public void OnTriggerStay()
|
||
|
{
|
||
|
CheckAssertionFor(CheckMethod.OnTriggerStay);
|
||
|
}
|
||
|
|
||
|
public void OnCollisionEnter()
|
||
|
{
|
||
|
CheckAssertionFor(CheckMethod.OnCollisionEnter);
|
||
|
}
|
||
|
|
||
|
public void OnCollisionExit()
|
||
|
{
|
||
|
CheckAssertionFor(CheckMethod.OnCollisionExit);
|
||
|
}
|
||
|
|
||
|
public void OnCollisionStay()
|
||
|
{
|
||
|
CheckAssertionFor(CheckMethod.OnCollisionStay);
|
||
|
}
|
||
|
|
||
|
public void OnTriggerEnter2D()
|
||
|
{
|
||
|
CheckAssertionFor(CheckMethod.OnTriggerEnter2D);
|
||
|
}
|
||
|
|
||
|
public void OnTriggerExit2D()
|
||
|
{
|
||
|
CheckAssertionFor(CheckMethod.OnTriggerExit2D);
|
||
|
}
|
||
|
|
||
|
public void OnTriggerStay2D()
|
||
|
{
|
||
|
CheckAssertionFor(CheckMethod.OnTriggerStay2D);
|
||
|
}
|
||
|
|
||
|
public void OnCollisionEnter2D()
|
||
|
{
|
||
|
CheckAssertionFor(CheckMethod.OnCollisionEnter2D);
|
||
|
}
|
||
|
|
||
|
public void OnCollisionExit2D()
|
||
|
{
|
||
|
CheckAssertionFor(CheckMethod.OnCollisionExit2D);
|
||
|
}
|
||
|
|
||
|
public void OnCollisionStay2D()
|
||
|
{
|
||
|
CheckAssertionFor(CheckMethod.OnCollisionStay2D);
|
||
|
}
|
||
|
|
||
|
private void CheckAssertionFor(CheckMethod checkMethod)
|
||
|
{
|
||
|
if (IsCheckMethodSelected(checkMethod))
|
||
|
{
|
||
|
Assertions.CheckAssertions(this);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public bool IsCheckMethodSelected(CheckMethod method)
|
||
|
{
|
||
|
return method == (checkMethods & method);
|
||
|
}
|
||
|
|
||
|
|
||
|
#region Assertion Component create methods
|
||
|
|
||
|
public static T Create<T>(CheckMethod checkOnMethods, GameObject gameObject, string propertyPath) where T : ActionBase
|
||
|
{
|
||
|
IAssertionComponentConfigurator configurator;
|
||
|
return Create<T>(out configurator, checkOnMethods, gameObject, propertyPath);
|
||
|
}
|
||
|
|
||
|
public static T Create<T>(out IAssertionComponentConfigurator configurator, CheckMethod checkOnMethods, GameObject gameObject, string propertyPath) where T : ActionBase
|
||
|
{
|
||
|
return CreateAssertionComponent<T>(out configurator, checkOnMethods, gameObject, propertyPath);
|
||
|
}
|
||
|
|
||
|
public static T Create<T>(CheckMethod checkOnMethods, GameObject gameObject, string propertyPath, GameObject gameObject2, string propertyPath2) where T : ComparerBase
|
||
|
{
|
||
|
IAssertionComponentConfigurator configurator;
|
||
|
return Create<T>(out configurator, checkOnMethods, gameObject, propertyPath, gameObject2, propertyPath2);
|
||
|
}
|
||
|
|
||
|
public static T Create<T>(out IAssertionComponentConfigurator configurator, CheckMethod checkOnMethods, GameObject gameObject, string propertyPath, GameObject gameObject2, string propertyPath2) where T : ComparerBase
|
||
|
{
|
||
|
var comparer = CreateAssertionComponent<T>(out configurator, checkOnMethods, gameObject, propertyPath);
|
||
|
comparer.compareToType = ComparerBase.CompareToType.CompareToObject;
|
||
|
comparer.other = gameObject2;
|
||
|
comparer.otherPropertyPath = propertyPath2;
|
||
|
return comparer;
|
||
|
}
|
||
|
|
||
|
public static T Create<T>(CheckMethod checkOnMethods, GameObject gameObject, string propertyPath, object constValue) where T : ComparerBase
|
||
|
{
|
||
|
IAssertionComponentConfigurator configurator;
|
||
|
return Create<T>(out configurator, checkOnMethods, gameObject, propertyPath, constValue);
|
||
|
}
|
||
|
|
||
|
public static T Create<T>(out IAssertionComponentConfigurator configurator, CheckMethod checkOnMethods, GameObject gameObject, string propertyPath, object constValue) where T : ComparerBase
|
||
|
{
|
||
|
var comparer = CreateAssertionComponent<T>(out configurator, checkOnMethods, gameObject, propertyPath);
|
||
|
if (constValue == null)
|
||
|
{
|
||
|
comparer.compareToType = ComparerBase.CompareToType.CompareToNull;
|
||
|
return comparer;
|
||
|
}
|
||
|
comparer.compareToType = ComparerBase.CompareToType.CompareToConstantValue;
|
||
|
comparer.ConstValue = constValue;
|
||
|
return comparer;
|
||
|
}
|
||
|
|
||
|
private static T CreateAssertionComponent<T>(out IAssertionComponentConfigurator configurator, CheckMethod checkOnMethods, GameObject gameObject, string propertyPath) where T : ActionBase
|
||
|
{
|
||
|
var ac = gameObject.AddComponent<AssertionComponent>();
|
||
|
ac.checkMethods = checkOnMethods;
|
||
|
var comparer = ScriptableObject.CreateInstance<T>();
|
||
|
ac.Action = comparer;
|
||
|
ac.Action.go = gameObject;
|
||
|
ac.Action.thisPropertyPath = propertyPath;
|
||
|
configurator = ac;
|
||
|
|
||
|
#if !UNITY_METRO
|
||
|
var stackTrace = new StackTrace(true);
|
||
|
var thisFileName = stackTrace.GetFrame(0).GetFileName();
|
||
|
for (int i = 1; i < stackTrace.FrameCount; i++)
|
||
|
{
|
||
|
var stackFrame = stackTrace.GetFrame(i);
|
||
|
if (stackFrame.GetFileName() != thisFileName)
|
||
|
{
|
||
|
string filePath = stackFrame.GetFileName().Substring(Application.dataPath.Length - "Assets".Length);
|
||
|
ac.m_CreatedInFilePath = filePath;
|
||
|
ac.m_CreatedInFileLine = stackFrame.GetFileLineNumber();
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
#endif // if !UNITY_METRO
|
||
|
return comparer;
|
||
|
}
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
#region AssertionComponentConfigurator
|
||
|
public int UpdateCheckStartOnFrame { set { checkAfterFrames = value; } }
|
||
|
public int UpdateCheckRepeatFrequency { set { repeatEveryFrame = value; } }
|
||
|
public bool UpdateCheckRepeat { set { repeatCheckFrame = value; } }
|
||
|
public float TimeCheckStartAfter { set { checkAfterTime = value; } }
|
||
|
public float TimeCheckRepeatFrequency { set { repeatEveryTime = value; } }
|
||
|
public bool TimeCheckRepeat { set { repeatCheckTime = value; } }
|
||
|
public AssertionComponent Component { get { return this; } }
|
||
|
#endregion
|
||
|
}
|
||
|
|
||
|
public interface IAssertionComponentConfigurator
|
||
|
{
|
||
|
/// <summary>
|
||
|
/// If the assertion is evaluated in Update, after how many frame should the evaluation start. Deafult is 1 (first frame)
|
||
|
/// </summary>
|
||
|
int UpdateCheckStartOnFrame { set; }
|
||
|
/// <summary>
|
||
|
/// If the assertion is evaluated in Update and UpdateCheckRepeat is true, how many frame should pass between evaluations
|
||
|
/// </summary>
|
||
|
int UpdateCheckRepeatFrequency { set; }
|
||
|
/// <summary>
|
||
|
/// If the assertion is evaluated in Update, should the evaluation be repeated after UpdateCheckRepeatFrequency frames
|
||
|
/// </summary>
|
||
|
bool UpdateCheckRepeat { set; }
|
||
|
|
||
|
/// <summary>
|
||
|
/// If the assertion is evaluated after a period of time, after how many seconds the first evaluation should be done
|
||
|
/// </summary>
|
||
|
float TimeCheckStartAfter { set; }
|
||
|
/// <summary>
|
||
|
/// If the assertion is evaluated after a period of time and TimeCheckRepeat is true, after how many seconds should the next evaluation happen
|
||
|
/// </summary>
|
||
|
float TimeCheckRepeatFrequency { set; }
|
||
|
/// <summary>
|
||
|
/// If the assertion is evaluated after a period, should the evaluation happen again after TimeCheckRepeatFrequency seconds
|
||
|
/// </summary>
|
||
|
bool TimeCheckRepeat { set; }
|
||
|
|
||
|
AssertionComponent Component { get; }
|
||
|
}
|
||
|
}
|