< Summary

Class:DCL.Helpers.VisualTestHelpers
Assembly:VisualTests
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/Tests/VisualTests/VisualTestHelpers.cs
Covered lines:88
Uncovered lines:37
Coverable lines:125
Total lines:315
Line coverage:70.4% (88 of 125)
Covered branches:0
Total branches:0

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
VisualTestHelpers()0%110100%
GenerateBaselineForTest()0%12300%
TakeSnapshot()0%330100%
TakeSnapshot()0%10.610081.82%
TakeSnapshotOrTest()0%330100%
TestSnapshot(...)0%4.074083.33%
TakeSnapshot()0%11.3511085.71%
ComputeImageAffinityPercentage(...)0%110100%
ComputeImageAffinityPercentage(...)0%14.869058.33%
DuplicateTextureAsReadable(...)0%110100%
ResizeTexture(...)0%2100%
IsSamePixel(...)0%660100%
RepositionVisualTestsCamera(...)0%3.073080%
RepositionVisualTestsCamera(...)0%110100%

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/Tests/VisualTests/VisualTestHelpers.cs

#LineLine coverage
 1using DCL.Configuration;
 2using System.Collections;
 3using System.IO;
 4using UnityEngine;
 5using UnityEngine.Assertions;
 6
 7namespace DCL.Helpers
 8{
 9    public static class VisualTestHelpers
 10    {
 111        public static string testImagesPath = Application.dataPath + "/../TestResources/VisualTests/CurrentTestImages/";
 12
 113        public static string baselineImagesPath =
 14            Application.dataPath + "/../TestResources/VisualTests/BaselineImages/";
 15
 116        public static bool generateBaseline = false;
 17        public static string currentTestName;
 18        public static int snapshotIndex;
 19
 20        public static IEnumerator GenerateBaselineForTest(IEnumerator test)
 21        {
 022            generateBaseline = true;
 023            yield return test;
 024            generateBaseline = false;
 025        }
 26
 27        /// <summary>
 28        /// This coroutine will take a visual test snapshot positioning the camera from shotPosition and pointing at sho
 29        /// Used in tandem with GenerateBaselineForTest(), TakeSnapshot will also generate the baseline test images.
 30        ///
 31        /// Snapshot name will be generated dinamically using the name set in InitVisualTestsScene() and an static count
 32        /// </summary>
 33        /// <param name="shotPosition">camera will be placed here.</param>
 34        /// <param name="shotTarget">camera will point towards here.</param>
 35        public static IEnumerator TakeSnapshot(Vector3? shotPosition = null, Vector3? shotTarget = null)
 36        {
 637            yield return TakeSnapshotOrTest(currentTestName + "_" + snapshotIndex + ".png", shotPosition, shotTarget);
 638            snapshotIndex++;
 639        }
 40
 41        public static IEnumerator TakeSnapshot(string snapshotName, Camera camera, Vector3? shotPosition = null, Vector3
 42        {
 643            if (shotPosition.HasValue || shotTarget.HasValue)
 44            {
 645                RepositionVisualTestsCamera(camera, shotPosition, shotTarget);
 46            }
 47
 648            yield return null;
 649            yield return null;
 50
 651            int snapshotsWidth = TestSettings.VISUAL_TESTS_SNAPSHOT_WIDTH;
 652            int snapshotsHeight = TestSettings.VISUAL_TESTS_SNAPSHOT_HEIGHT;
 53
 654            if (generateBaseline || !File.Exists(baselineImagesPath + snapshotName))
 55            {
 056                yield return TakeSnapshot(baselineImagesPath, snapshotName, camera,
 57                    snapshotsWidth, snapshotsHeight);
 058            }
 59            else
 60            {
 661                yield return TakeSnapshot(testImagesPath, snapshotName, camera, snapshotsWidth, snapshotsHeight);
 62            }
 663        }
 64
 65        /// <summary>
 66        /// This coroutine will take a visual test snapshot positioning the camera from shotPosition and pointing at sho
 67        /// Used in tandem with GenerateBaselineForTest(), TakeSnapshot will also generate the baseline test images.
 68        /// </summary>
 69        /// <param name="snapshotName">name used for saving the visual test file</param>
 70        /// <param name="shotPosition">camera will be placed here.</param>
 71        /// <param name="shotTarget">camera will point towards here.</param>
 72        public static IEnumerator TakeSnapshotOrTest(string snapshotName, Vector3? shotPosition = null, Vector3? shotTar
 73        {
 674            yield return TakeSnapshot(snapshotName, VisualTestController.i.camera, shotPosition, shotTarget);
 75
 676            TestSnapshot(baselineImagesPath + snapshotName, testImagesPath + snapshotName, TestSettings.VISUAL_TESTS_APP
 677        }
 78
 79        public static bool TestSnapshot(string baselineImagePathWithFilename, string testImagePathWithFilename, float ra
 80        {
 681            if (generateBaseline || !File.Exists(baselineImagePathWithFilename))
 082                return false;
 83
 684            float ratioResult =
 85                ComputeImageAffinityPercentage(baselineImagePathWithFilename, testImagePathWithFilename);
 86
 687            if (assert)
 88            {
 689                Assert.IsTrue(ratioResult > ratio,
 90                    $"{Path.GetFileName(baselineImagePathWithFilename)} has {ratioResult}% affinity, the minimum is {rat
 91            }
 92
 693            return ratioResult > ratio;
 94        }
 95
 96        /// <summary>
 97        /// This coroutine will take a visual test snapshot using the camera provided, with the given image size.
 98        /// The image will be saved to disk as png.
 99        /// </summary>
 100        /// <param name="snapshotPath">Path to the directory where the image will be saved. Will be created if not exist
 101        /// <param name="snapshotName">output filename, it should include the png extension</param>
 102        /// <param name="camera">camera used to take the shot</param>
 103        /// <param name="width">Width of the final image</param>
 104        /// <param name="height">Height of the final image</param>
 105        public static IEnumerator TakeSnapshot(string snapshotPath, string snapshotName, Camera camera, int width,
 106            int height)
 107        {
 6108            if (string.IsNullOrEmpty(snapshotName) || camera == null)
 109            {
 0110                Debug.Log("snapshot name or camera is not valid. Snapshot aborted.");
 0111                yield break;
 112            }
 113
 6114            var previousQualityLevel = QualitySettings.GetQualityLevel();
 6115            QualitySettings.SetQualityLevel((int) QualityLevel.Good, true);
 116
 6117            string finalPath = snapshotPath + snapshotName;
 118
 6119            if (File.Exists(finalPath))
 120            {
 0121                File.Delete(finalPath);
 122
 123                // Just in case, wait until the file is deleted
 0124                yield return new DCL.WaitUntil(() => { return !File.Exists(finalPath); }, 10f);
 125            }
 126
 127            // We should only read the screen buffer after rendering is complete
 6128            yield return null;
 129
 6130            RenderTexture renderTexture = new RenderTexture(width, height, 24);
 6131            camera.targetTexture = renderTexture;
 6132            camera.Render();
 133
 6134            RenderTexture.active = renderTexture;
 6135            Texture2D currentSnapshot = new Texture2D(width, height, TextureFormat.RGB24, false);
 6136            currentSnapshot.ReadPixels(new Rect(0, 0, width, height), 0, 0);
 6137            currentSnapshot.Apply();
 138
 6139            yield return null;
 140
 6141            if (!Directory.Exists(snapshotPath))
 142            {
 1143                Directory.CreateDirectory(snapshotPath);
 144            }
 145
 6146            byte[] bytes = currentSnapshot.EncodeToPNG();
 6147            File.WriteAllBytes(finalPath, bytes);
 148
 149            // Just in case, wait until the file is created
 12150            yield return new DCL.WaitUntil(() => { return File.Exists(finalPath); }, 10f);
 151
 6152            RenderTexture.active = null;
 6153            renderTexture.Release();
 154
 6155            yield return new WaitForSeconds(0.2f);
 156
 6157            QualitySettings.SetQualityLevel(previousQualityLevel, true);
 6158        }
 159
 160        public static float ComputeImageAffinityPercentage(string baselineImagePathWithFilename,
 161            string testImagePathWithFilename)
 162        {
 6163            Texture2D baselineSnapshot = new Texture2D(TestSettings.VISUAL_TESTS_SNAPSHOT_WIDTH,
 164                TestSettings.VISUAL_TESTS_SNAPSHOT_HEIGHT, TextureFormat.RGB24, false);
 6165            baselineSnapshot.LoadImage(File.ReadAllBytes(baselineImagePathWithFilename));
 166
 6167            Texture2D currentSnapshot = new Texture2D(TestSettings.VISUAL_TESTS_SNAPSHOT_WIDTH,
 168                TestSettings.VISUAL_TESTS_SNAPSHOT_HEIGHT, TextureFormat.RGB24, false);
 6169            currentSnapshot.LoadImage(File.ReadAllBytes(testImagePathWithFilename));
 170
 6171            string finalDiffPath = Path.GetDirectoryName(testImagePathWithFilename) + "/" +
 172                                   Path.GetFileNameWithoutExtension(testImagePathWithFilename) + "_diff" +
 173                                   Path.GetExtension(testImagePathWithFilename);
 174
 6175            return ComputeImageAffinityPercentage(baselineSnapshot, currentSnapshot, finalDiffPath);
 176        }
 177
 178        /// <summary>
 179        /// This will compare the pixels of two images in order to make visual tests.
 180        /// </summary>
 181        /// <param name="baselineImage">Reference or "golden" image</param>
 182        /// <param name="testImage">Image to compare</param>
 183        /// <param name="diffImagePath"></param>
 184        /// <returns>Affinity percentage</returns>
 185        public static float ComputeImageAffinityPercentage(Texture2D baselineImage, Texture2D testImage,
 186            string diffImagePath)
 187        {
 6188            baselineImage = DuplicateTextureAsReadable(baselineImage);
 6189            testImage = DuplicateTextureAsReadable(testImage);
 190
 6191            if (string.IsNullOrEmpty(diffImagePath))
 192            {
 0193                Debug.Log("diff image path is not valid. Image affinity percentage check aborted.");
 194
 0195                return -1;
 196            }
 197
 6198            if (baselineImage.width != testImage.width || baselineImage.height != testImage.height)
 199            {
 0200                Debug.Log("CAN'T COMPARE IMAGES WITH DIFFERENT DIMENSIONS:");
 0201                Debug.Log("baseline image dimensions: " + baselineImage.width + "," + baselineImage.height);
 0202                Debug.Log("test image dimensions: " + testImage.width + "," + testImage.height);
 203
 0204                return -1;
 205            }
 206
 6207            Color32[] baselineImagePixels = baselineImage.GetPixels32();
 6208            Color32[] testImagePixels = testImage.GetPixels32();
 6209            Color32[] diffImagePixels = new Color32[testImagePixels.Length];
 6210            Color32 diffColor = new Color32(255, 0, 0, 255);
 6211            int differentPixels = 0;
 212
 11059212213            for (int i = 0; i < testImagePixels.Length; i++)
 214            {
 5529600215                if (!IsSamePixel(testImagePixels[i], baselineImagePixels[i],
 216                    TestSettings.VISUAL_TESTS_PIXELS_CHECK_THRESHOLD))
 217                {
 57645218                    differentPixels++;
 57645219                    diffImagePixels[i] = diffColor;
 57645220                }
 221                else
 222                {
 5471955223                    diffImagePixels[i] = baselineImagePixels[i];
 224                }
 225            }
 226
 227            // Calculate Image Affinity
 6228            float imageAffinity = ((testImagePixels.Length - differentPixels) * 100) / testImagePixels.Length;
 229
 230            // Save diff image
 6231            if (imageAffinity < TestSettings.VISUAL_TESTS_APPROVED_AFFINITY)
 232            {
 0233                Texture2D diffImage = new Texture2D(baselineImage.width, baselineImage.height);
 0234                diffImage.SetPixels32(diffImagePixels);
 0235                diffImage.Apply();
 0236                byte[] bytes = diffImage.EncodeToPNG();
 0237                File.WriteAllBytes(diffImagePath, bytes);
 0238            }
 6239            else if (File.Exists(diffImagePath))
 240            {
 0241                File.Delete(diffImagePath);
 242
 0243                if (File.Exists(diffImagePath + ".meta"))
 0244                    File.Delete(diffImagePath + ".meta");
 245            }
 246
 6247            return imageAffinity;
 248        }
 249
 250        public static Texture2D DuplicateTextureAsReadable(Texture2D source)
 251        {
 12252            RenderTexture renderTex = RenderTexture.GetTemporary(
 253                source.width,
 254                source.height,
 255                0,
 256                RenderTextureFormat.Default,
 257                RenderTextureReadWrite.Linear);
 258
 12259            Graphics.Blit(source, renderTex);
 260
 12261            RenderTexture previous = RenderTexture.active;
 12262            RenderTexture.active = renderTex;
 263
 12264            Texture2D readableText = new Texture2D(source.width, source.height);
 12265            readableText.ReadPixels(new Rect(0, 0, renderTex.width, renderTex.height), 0, 0);
 12266            readableText.Apply();
 267
 12268            RenderTexture.active = previous;
 12269            RenderTexture.ReleaseTemporary(renderTex);
 270
 12271            return readableText;
 272        }
 273
 274        public static Texture2D ResizeTexture(Texture2D source, int newWidth, int newHeight)
 275        {
 0276            source.filterMode = FilterMode.Point;
 277
 0278            RenderTexture renderTex = RenderTexture.GetTemporary(newWidth, newHeight);
 279
 0280            renderTex.filterMode = FilterMode.Point;
 0281            RenderTexture.active = renderTex;
 0282            Graphics.Blit(source, renderTex);
 283
 0284            Texture2D newTex = new Texture2D(newWidth, newHeight);
 0285            newTex.ReadPixels(new Rect(0, 0, newWidth, newWidth), 0, 0);
 0286            newTex.Apply();
 287
 0288            RenderTexture.active = null;
 289
 0290            return newTex;
 291        }
 292
 293        public static bool IsSamePixel(Color32 pixelA, Color32 pixelB, float checkThreshold)
 294        {
 5529600295            return (pixelA.r > pixelB.r - checkThreshold && pixelA.r < pixelB.r + checkThreshold) &&
 296                   (pixelA.g > pixelB.g - checkThreshold && pixelA.g < pixelB.g + checkThreshold) &&
 297                   (pixelA.b > pixelB.b - checkThreshold && pixelA.b < pixelB.b + checkThreshold);
 298        }
 299
 300        public static void RepositionVisualTestsCamera(Transform cameraTransform, Vector3? position = null, Vector3? tar
 301        {
 6302            if (position.HasValue)
 303            {
 6304                cameraTransform.position = position.Value;
 305            }
 306
 6307            if (target.HasValue)
 308            {
 0309                cameraTransform.forward = target.Value - cameraTransform.position;
 310            }
 6311        }
 312
 12313        public static void RepositionVisualTestsCamera(Camera camera, Vector3? position = null, Vector3? target = null) 
 314    }
 315}