using System; using System.Collections; using System.Collections.Generic; using System.Linq; using UnityEngine; using System.IO; using UnityEditor; using NUnit.Framework; using UnityEngine.TestTools; namespace UnityEngine.Experimental.Rendering { public class TestFrameworkTools { public static readonly string s_RootPath = Directory.GetParent(Directory.GetFiles(Application.dataPath, "SRPMARKER", SearchOption.AllDirectories).First()).ToString(); // path where the tests live public static string[] s_Path = { "Tests", "GraphicsTests", "RenderPipeline" }; public static Dictionary renderPipelineAssets = new Dictionary() { { "HDRP", "Tests/GraphicsTests/RenderPipeline/HDRenderPipeline/CommonAssets/HDRP_GraphicTests_Asset.asset" }, { "LWRP", "Tests/GraphicsTests/RenderPipeline/LightweightPipeline/LightweightPipelineAsset.asset" } }; // Renderpipeline assets used for the tests public static Dictionary renderPipelineScenesFolder = new Dictionary() { { "HDRP", "HDRenderPipeline/Scenes" }, { "LWRP", "LightweightPipeline" } }; // info that gets generated for use // in a dod way public struct TestInfo { public string name; public float threshold; public string relativePath; public string templatePath; public int frameWait; public override string ToString() { return name; } } // collect the scenes that we can use public static class CollectScenes { public static IEnumerable HDRP { get { return GetScenesForPipelineID("HDRP"); } } public static IEnumerable HDRP_Params { get { return GetScenesForPipelineID("HDRP", true); } } public static IEnumerable LWRP { get { return GetScenesForPipelineID("LWRP"); } } public static IEnumerable GetScenesForPipelineID(string _pipelineID, bool fixtureParam = false) { return GetScenesForPipeline(renderPipelineScenesFolder[_pipelineID]); } public static IEnumerable GetScenesForPipeline(string _pipelinePath, bool fixtureParam = false) { var absoluteScenesPath = s_Path.Aggregate(s_RootPath, Path.Combine); var filesPath = Path.Combine(absoluteScenesPath, _pipelinePath); // find all the scenes var allPaths = Directory.GetFiles(filesPath, "*.unity", SearchOption.AllDirectories); // Convert to List for easy sorting in alphabetical order List allPaths_List = new List(allPaths); allPaths_List.Sort(); // construct all the needed test infos for (int i = 0; i < allPaths_List.Count; ++i) { var path = allPaths_List[i]; var p = new FileInfo(path); var split = s_Path.Aggregate("", Path.Combine); split = string.Format("{0}{1}", split, Path.DirectorySeparatorChar); var splitPaths = p.FullName.Split(new[] { split }, StringSplitOptions.RemoveEmptyEntries); TestInfo testInfo = new TestInfo() { name = p.Name, relativePath = splitPaths.Last(), templatePath = splitPaths.Last(), threshold = 0.02f, frameWait = 100 }; if (fixtureParam) yield return new TestFixtureData(testInfo); else yield return testInfo; } } } public static class CollectScenesPlayMode { public static IEnumerable HDRP { get { return GetScenesForPipelineID("HDRP"); } } public static IEnumerable HDRP_Param { get { return GetScenesForPipelineID("HDRP", true); } } public static IEnumerable LWRP { get { return GetScenesForPipelineID("LWRP"); } } public static IEnumerable GetScenesForPipelineID(string _pipelineID, bool fixtureParam = false) { return GetScenesForPipeline(renderPipelineScenesFolder[_pipelineID]); } public static IEnumerable GetScenesForPipeline(string _pipelinePath, bool fixtureParam = false) { #if UNITY_EDITOR var absoluteScenesPath = s_Path.Aggregate("Assets", Path.Combine); var filesPath = Path.Combine(absoluteScenesPath, _pipelinePath); string listFilePath = Path.Combine(filesPath, "EditorPlayModeTests.asset"); EditorPlayModeTests listFile = (EditorPlayModeTests) AssetDatabase.LoadMainAssetAtPath(listFilePath); if ( listFile == null) { yield return null; } else { foreach (string path in listFile.scenesPath) { var p = new FileInfo(path); var split = s_Path.Aggregate("", Path.Combine); split = string.Format("{0}{1}", split, Path.DirectorySeparatorChar); var splitPaths = p.FullName.Split(new[] { split }, StringSplitOptions.RemoveEmptyEntries); TestInfo testInfo = new TestInfo { name = p.Name, relativePath = path, templatePath = splitPaths.Last(), threshold = 0.02f, frameWait = 100 }; if (fixtureParam) yield return new TestFixtureData(testInfo); else yield return testInfo; } } #else yield return "null"; #endif } } // compare textures, use RMS for this public static bool CompareTextures(Texture2D fromDisk, Texture2D captured, float threshold) { if (fromDisk == null || captured == null) return false; if (fromDisk.width != captured.width || fromDisk.height != captured.height) return false; var pixels1 = fromDisk.GetPixels(); var pixels2 = captured.GetPixels(); if (pixels1.Length != pixels2.Length) return false; int numberOfPixels = pixels1.Length; float sumOfSquaredColorDistances = 0; for (int i = 0; i < numberOfPixels; i++) { Color p1 = pixels1[i]; Color p2 = pixels2[i]; Color diff = p1 - p2; diff = diff * diff; sumOfSquaredColorDistances += (diff.r + diff.g + diff.b) / 3.0f; } float rmse = Mathf.Sqrt(sumOfSquaredColorDistances / numberOfPixels); return rmse < threshold; } public static Texture2D RenderSetupToTexture( SetupSceneForRenderPipelineTest _testSetup) { // Setup Render Target Camera testCamera = _testSetup.cameraToUse; var rtDesc = new RenderTextureDescriptor( _testSetup.width, _testSetup.height, (_testSetup.hdr && testCamera.allowHDR) ? RenderTextureFormat.ARGBHalf : RenderTextureFormat.ARGB32, 24); #if UNITY_EDITOR rtDesc.sRGB = PlayerSettings.colorSpace == ColorSpace.Linear; #endif rtDesc.msaaSamples = _testSetup.msaaSamples; // render the scene var tempTarget = RenderTexture.GetTemporary(rtDesc); var oldTarget = _testSetup.cameraToUse.targetTexture; _testSetup.cameraToUse.targetTexture = tempTarget; _testSetup.cameraToUse.Render(); _testSetup.cameraToUse.targetTexture = oldTarget; // Readback the rendered texture var oldActive = RenderTexture.active; RenderTexture.active = tempTarget; var captured = new Texture2D(tempTarget.width, tempTarget.height, TextureFormat.RGB24, false); captured.ReadPixels(new Rect(0, 0, _testSetup.width, _testSetup.height), 0, 0); RenderTexture.active = oldActive; return captured; } public static bool FindReferenceImage(TestInfo _testInfo, ref Texture2D _fromDisk, Texture2D _captured, ref string _dumpFileLocation) { var templatePath = Path.Combine(s_RootPath, "ImageTemplates"); // find the reference image _dumpFileLocation = Path.Combine(templatePath, string.Format("{0}.{1}", _testInfo.templatePath, "png")); Debug.Log("Template file at: " + _dumpFileLocation); if (!File.Exists(_dumpFileLocation)) { // no reference exists, create it var fileInfo = new FileInfo(_dumpFileLocation); fileInfo.Directory.Create(); var generated = _captured.EncodeToPNG(); File.WriteAllBytes(_dumpFileLocation, generated); return false; } var template = File.ReadAllBytes(_dumpFileLocation); _fromDisk.LoadImage(template, false); return true; } public static class AssertFix { public static void TestWithMessages( bool? _comparison, string _fail = "Test failed", string _pass = null ) { if (_comparison.HasValue) { if (_comparison.Value) NUnit.Framework.Assert.IsTrue(true, _pass); else throw new System.Exception(_fail); } else throw new System.Exception("Test comparison is null."); } } } }