< Summary

Class:DCL.Helpers.WaitForAllMessagesProcessed
Assembly:TestHelpers
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/Tests/TestHelpers.cs
Covered lines:1
Uncovered lines:0
Coverable lines:1
Total lines:1197
Line coverage:100% (1 of 1)
Covered branches:0
Total branches:0

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/Tests/TestHelpers.cs

#LineLine coverage
 1using DCL.Components;
 2using DCL.Configuration;
 3using DCL.Controllers;
 4using DCL.Interface;
 5using DCL.Models;
 6using Google.Protobuf;
 7using Newtonsoft.Json;
 8using System;
 9using System.Collections;
 10using System.Collections.Generic;
 11using System.Linq;
 12using System.Reflection;
 13using UnityEngine;
 14using UnityEngine.Assertions;
 15using UnityEngine.EventSystems;
 16using UnityEngine.SceneManagement;
 17using Color = UnityEngine.Color;
 18using Object = System.Object;
 19
 20namespace DCL.Helpers
 21{
 22    public class WaitForAllMessagesProcessed : CustomYieldInstruction
 23    {
 3024        public override bool keepWaiting => Environment.i.messaging.manager.hasPendingMessages;
 25    }
 26
 27    // NOTE(Brian): Attribute used to determine if tests are visual. Those tests will be run to generate the baseline im
 28    [AttributeUsage(AttributeTargets.Method)]
 29    public class VisualTestAttribute : Attribute { }
 30
 31    public static class TestHelpers
 32    {
 33        public static int testSceneIteration;
 34        public const string testingSceneName = "DCL_Testing_";
 35
 36        public static string CreateSceneMessage(string sceneId, string tag, string method, string payload) { return $"{s
 37
 38        static int entityCounter = 123;
 39        static int disposableIdCounter = 123;
 40
 41        public static PB_Transform GetPBTransform(Vector3 position, Quaternion rotation, Vector3 scale)
 42        {
 43            PB_Transform pbTranf = new PB_Transform();
 44            pbTranf.Position = new PB_Vector3();
 45            pbTranf.Position.X = position.x;
 46            pbTranf.Position.Y = position.y;
 47            pbTranf.Position.Z = position.z;
 48            pbTranf.Rotation = new PB_Quaternion();
 49            pbTranf.Rotation.X = rotation.x;
 50            pbTranf.Rotation.Y = rotation.y;
 51            pbTranf.Rotation.Z = rotation.z;
 52            pbTranf.Rotation.W = rotation.w;
 53            pbTranf.Scale = new PB_Vector3();
 54            pbTranf.Scale.X = scale.x;
 55            pbTranf.Scale.Y = scale.y;
 56            pbTranf.Scale.Z = scale.z;
 57            return pbTranf;
 58        }
 59
 60        public static PB_Transform GetPBTransformFromModelJson(string json)
 61        {
 62            DCLTransform.Model transfModel = JsonUtility.FromJson<DCLTransform.Model>(json);
 63            PB_Transform pbTranf = GetPBTransform(transfModel.position, transfModel.rotation, transfModel.scale);
 64            return pbTranf;
 65        }
 66
 67        public static IDCLEntity CreateSceneEntity(ParcelScene scene)
 68        {
 69            Assert.IsNotNull(scene, "Can't create entity for null scene!");
 70
 71            entityCounter++;
 72            string id = $"{entityCounter}";
 73            return scene.CreateEntity(id);
 74        }
 75
 76        public static IDCLEntity CreateSceneEntity(ParcelScene scene, string id) { return scene.CreateEntity(id); }
 77
 78        public static void RemoveSceneEntity(ParcelScene scene, string id) { scene.RemoveEntity(id); }
 79
 80        public static void RemoveSceneEntity(ParcelScene scene, IDCLEntity entity) { scene.RemoveEntity(entity.entityId)
 81
 82        public static T EntityComponentCreate<T, K>(ParcelScene scene, IDCLEntity entity, K model,
 83            CLASS_ID_COMPONENT classId = CLASS_ID_COMPONENT.NONE)
 84            where T : BaseComponent
 85            where K : new()
 86        {
 87            var factory = Environment.i.world.componentFactory as RuntimeComponentFactory;
 88            IPoolableComponentFactory poolableFactory = factory.poolableComponentFactory;
 89            int inferredId = (int) poolableFactory.GetIdForType<T>();
 90
 91            int componentClassId = classId == CLASS_ID_COMPONENT.NONE
 92                ? (int) inferredId
 93                : (int) classId;
 94
 95            string data;
 96
 97            if (classId == CLASS_ID_COMPONENT.TRANSFORM)
 98            {
 99                PB_Transform transf = GetPBTransformFromModelJson(JsonUtility.ToJson(model));
 100                data = System.Convert.ToBase64String(transf.ToByteArray());
 101            }
 102            else
 103            {
 104                data = JsonUtility.ToJson(model);
 105            }
 106
 107            return scene.EntityComponentCreateOrUpdate(
 108                entity.entityId,
 109                (CLASS_ID_COMPONENT) componentClassId,
 110                data) as T;
 111        }
 112
 113        public static Coroutine EntityComponentUpdate<T, K>(T component, K model = null)
 114            where T : BaseComponent
 115            where K : class, new()
 116        {
 117            if (model == null)
 118            {
 119                model = new K();
 120            }
 121
 122            var factory = Environment.i.world.componentFactory as RuntimeComponentFactory;
 123            IPoolableComponentFactory poolableFactory = factory.poolableComponentFactory;
 124            int inferredId = (int) poolableFactory.GetIdForType<T>();
 125
 126            CLASS_ID_COMPONENT classId = (CLASS_ID_COMPONENT) inferredId;
 127
 128            ParcelScene scene = component.scene as ParcelScene;
 129            scene.EntityComponentUpdate(component.entity, classId, JsonUtility.ToJson(model));
 130
 131            return component.routine;
 132        }
 133
 134        public static void SetEntityParent(ParcelScene scene, IDCLEntity child, IDCLEntity parent) { scene.SetEntityPare
 135
 136        public static void SetEntityParent(ParcelScene scene, string childEntityId, string parentEntityId) { scene.SetEn
 137
 138        public static DCLTexture CreateDCLTexture(ParcelScene scene,
 139            string url,
 140            DCLTexture.BabylonWrapMode wrapMode = DCLTexture.BabylonWrapMode.CLAMP,
 141            FilterMode filterMode = FilterMode.Bilinear)
 142        {
 143            return SharedComponentCreate<DCLTexture, DCLTexture.Model>
 144            (
 145                scene,
 146                DCL.Models.CLASS_ID.TEXTURE,
 147                new DCLTexture.Model
 148                {
 149                    src = url,
 150                    wrap = wrapMode,
 151                    samplingMode = filterMode,
 152                }
 153            );
 154        }
 155
 156        public static Coroutine SharedComponentUpdate<T>(T component, BaseModel model)
 157            where T : BaseDisposable
 158        {
 159            ParcelScene scene = component.scene as ParcelScene;
 160            scene.SharedComponentUpdate(component.id, model);
 161
 162            return component.routine;
 163        }
 164
 165        public static Coroutine SharedComponentUpdate<T, K>(T component, K model = null)
 166            where T : ISharedComponent
 167            where K : class, new()
 168        {
 169            if (model == null)
 170            {
 171                model = new K();
 172            }
 173
 174            ParcelScene scene = component.scene as ParcelScene;
 175            scene.SharedComponentUpdate(component.id, JsonUtility.ToJson(model));
 176
 177            if (component is IDelayedComponent delayedComponent)
 178                return delayedComponent.routine;
 179
 180            return null;
 181        }
 182
 183        public static T SharedComponentCreate<T, K>(ParcelScene scene, CLASS_ID id, K model = null)
 184            where T : BaseDisposable
 185            where K : class, new()
 186        {
 187            if (model == null)
 188            {
 189                model = new K();
 190            }
 191
 192            disposableIdCounter++;
 193
 194            string uniqueId = GetComponentUniqueId(scene, "material", (int) id, "-shared-" + disposableIdCounter);
 195
 196            T result = scene.SharedComponentCreate(uniqueId, (int) id) as T;
 197
 198            Assert.IsNotNull(result, "class-id mismatch!");
 199
 200            scene.SharedComponentUpdate(uniqueId, JsonUtility.ToJson(model));
 201
 202            return result;
 203        }
 204
 205        public static void SharedComponentDispose(BaseDisposable component)
 206        {
 207            ParcelScene scene = component.scene as ParcelScene;
 208            scene.SharedComponentDispose(component.id);
 209        }
 210
 211        public static void SharedComponentAttach(BaseDisposable component, IDCLEntity entity)
 212        {
 213            ParcelScene scene = entity.scene as ParcelScene;
 214            scene.SharedComponentAttach(
 215                entity.entityId,
 216                component.id
 217            );
 218        }
 219
 220        public static void SetEntityTransform(ParcelScene scene, IDCLEntity entity, DCLTransform.Model model) { SetEntit
 221
 222        public static void SetEntityTransform(ParcelScene scene, IDCLEntity entity) { SetEntityTransform(scene, entity, 
 223
 224        public static void SetEntityTransform(ParcelScene scene, IDCLEntity entity, Vector3 position, Quaternion rotatio
 225        {
 226            PB_Transform pB_Transform = GetPBTransform(position, rotation, scale);
 227            scene.EntityComponentCreateOrUpdate(
 228                entity.entityId,
 229                CLASS_ID_COMPONENT.TRANSFORM,
 230                System.Convert.ToBase64String(pB_Transform.ToByteArray())
 231            );
 232        }
 233
 234        public static TextShape InstantiateEntityWithTextShape(ParcelScene scene, Vector3 position, TextShape.Model mode
 235        {
 236            IDCLEntity entity = CreateSceneEntity(scene);
 237            string componentId =
 238                GetComponentUniqueId(scene, "textShape", (int) CLASS_ID_COMPONENT.TEXT_SHAPE, entity.entityId);
 239
 240            TextShape textShape = EntityComponentCreate<TextShape, TextShape.Model>(scene, entity, model);
 241
 242            SetEntityTransform(scene, entity);
 243
 244            return textShape;
 245        }
 246
 247        public static GLTFShape AttachGLTFShape(IDCLEntity entity, ParcelScene scene, Vector3 position, GLTFShape.Model 
 248        {
 249            string componentId = GetComponentUniqueId(scene, "gltfShape", (int) CLASS_ID.GLTF_SHAPE, entity.entityId);
 250            GLTFShape gltfShape = SharedComponentCreate<GLTFShape, GLTFShape.Model>(scene, CLASS_ID.GLTF_SHAPE, model);
 251
 252            SetEntityTransform(scene, entity, position, Quaternion.identity, Vector3.one);
 253            SharedComponentAttach(gltfShape, entity);
 254            return gltfShape;
 255        }
 256
 257        public static GLTFShape CreateEntityWithGLTFShape(ParcelScene scene, Vector3 position, string url)
 258        {
 259            IDCLEntity entity = null;
 260            return CreateEntityWithGLTFShape(scene, position, new GLTFShape.Model() { src = url }, out entity);
 261        }
 262
 263        public static GLTFShape CreateEntityWithGLTFShape(ParcelScene scene, Vector3 position, string url,
 264            out IDCLEntity entity)
 265        {
 266            return CreateEntityWithGLTFShape(scene, position, new GLTFShape.Model() { src = url }, out entity);
 267        }
 268
 269        public static GLTFShape CreateEntityWithGLTFShape(ParcelScene scene, Vector3 position, GLTFShape.Model model)
 270        {
 271            IDCLEntity entity = null;
 272            return CreateEntityWithGLTFShape(scene, position, model, out entity);
 273        }
 274
 275        public static GLTFShape CreateEntityWithGLTFShape(ParcelScene scene, Vector3 position, GLTFShape.Model model,
 276            out IDCLEntity entity)
 277        {
 278            entity = CreateSceneEntity(scene);
 279            GLTFShape gltfShape = AttachGLTFShape(entity, scene, position, model);
 280            return gltfShape;
 281        }
 282
 283        public static BoxShape CreateEntityWithBoxShape(ParcelScene scene, Vector3 position,
 284            BoxShape.Model model = null)
 285        {
 286            return CreateEntityWithPrimitive<BoxShape, BoxShape.Model>(scene, position, CLASS_ID.BOX_SHAPE, model);
 287        }
 288
 289        public static BoxShape CreateEntityWithBoxShape(ParcelScene scene, Vector3 position, bool visible) { return Crea
 290
 291        public static SphereShape CreateEntityWithSphereShape(ParcelScene scene, Vector3 position,
 292            SphereShape.Model model = null)
 293        {
 294            return CreateEntityWithPrimitive<SphereShape, SphereShape.Model>(scene, position, CLASS_ID.SPHERE_SHAPE,
 295                model);
 296        }
 297
 298        public static SphereShape CreateEntityWithSphereShape(ParcelScene scene, Vector3 position, bool visible) { retur
 299
 300        public static PlaneShape CreateEntityWithPlaneShape(ParcelScene scene, Vector3 position,
 301            PlaneShape.Model model = null)
 302        {
 303            return CreateEntityWithPrimitive<PlaneShape, PlaneShape.Model>(scene, position, CLASS_ID.PLANE_SHAPE,
 304                model);
 305        }
 306
 307        public static PlaneShape CreateEntityWithPlaneShape(ParcelScene scene, Vector3 position, bool visible) { return 
 308
 309        public static CylinderShape CreateEntityWithCylinderShape(ParcelScene scene, Vector3 position,
 310            CylinderShape.Model model = null)
 311        {
 312            return CreateEntityWithPrimitive<CylinderShape, CylinderShape.Model>(scene, position,
 313                CLASS_ID.CYLINDER_SHAPE, model);
 314        }
 315
 316        public static CylinderShape CreateEntityWithCylinderShape(ParcelScene scene, Vector3 position, bool visible) { r
 317
 318        public static ConeShape CreateEntityWithConeShape(ParcelScene scene, Vector3 position,
 319            ConeShape.Model model = null)
 320        {
 321            return CreateEntityWithPrimitive<ConeShape, ConeShape.Model>(scene, position, CLASS_ID.CONE_SHAPE, model);
 322        }
 323
 324        public static ConeShape CreateEntityWithConeShape(ParcelScene scene, Vector3 position, bool visible) { return Cr
 325
 326        private static T CreateEntityWithPrimitive<T, K>(ParcelScene scene, Vector3 position, CLASS_ID classId,
 327            K model = null)
 328            where T : ParametrizedShape<K>
 329            where K : BaseShape.Model, new()
 330        {
 331            if (model == null)
 332            {
 333                model = new K();
 334            }
 335
 336            IDCLEntity entity = CreateSceneEntity(scene);
 337            T shape = SharedComponentCreate<T, K>(scene, classId, model);
 338            SharedComponentAttach(shape, entity);
 339            SetEntityTransform(scene, entity, position, Quaternion.identity, Vector3.one);
 340            return shape;
 341        }
 342
 343        public static BasicMaterial CreateEntityWithBasicMaterial(ParcelScene scene, BasicMaterial.Model model,
 344            out IDCLEntity entity)
 345        {
 346            return CreateEntityWithBasicMaterial(scene, model, Vector3.zero, out entity);
 347        }
 348
 349        public static BasicMaterial CreateEntityWithBasicMaterial(ParcelScene scene, BasicMaterial.Model model, Vector3 
 350            out IDCLEntity entity)
 351        {
 352            InstantiateEntityWithShape<BoxShape, BoxShape.Model>(scene, DCL.Models.CLASS_ID.BOX_SHAPE, position,
 353                out entity);
 354            BasicMaterial material =
 355                SharedComponentCreate<BasicMaterial, BasicMaterial.Model>(scene, CLASS_ID.BASIC_MATERIAL, model);
 356            SharedComponentAttach(material, entity);
 357            return material;
 358        }
 359
 360        public static PBRMaterial CreateEntityWithPBRMaterial(ParcelScene scene, PBRMaterial.Model model,
 361            out IDCLEntity entity)
 362        {
 363            return CreateEntityWithPBRMaterial(scene, model, Vector3.zero, out entity);
 364        }
 365
 366        public static PBRMaterial CreateEntityWithPBRMaterial(ParcelScene scene, PBRMaterial.Model model, Vector3 positi
 367            out IDCLEntity entity)
 368        {
 369            InstantiateEntityWithShape<BoxShape, BoxShape.Model>(scene, CLASS_ID.BOX_SHAPE, position,
 370                out entity);
 371            PBRMaterial material =
 372                SharedComponentCreate<PBRMaterial, PBRMaterial.Model>(scene, CLASS_ID.PBR_MATERIAL, model);
 373            SharedComponentAttach(material, entity);
 374            return material;
 375        }
 376
 377        public static T InstantiateEntityWithShape<T, K>(ParcelScene scene, DCL.Models.CLASS_ID classId,
 378            Vector3 position, out IDCLEntity entity, K model = null)
 379            where T : BaseShape
 380            where K : class, new()
 381        {
 382            if (model == null)
 383            {
 384                model = new K();
 385            }
 386
 387            entity = CreateSceneEntity(scene);
 388            string shapeId = "";
 389
 390            shapeId = CreateAndSetShape(scene, entity.entityId, classId, JsonConvert.SerializeObject(model));
 391
 392            T shape = scene.disposableComponents[shapeId] as T;
 393
 394            SetEntityTransform(scene, entity, position, Quaternion.identity, Vector3.one);
 395
 396            return shape;
 397        }
 398
 399        public static void InstantiateEntityWithShape(ParcelScene scene, string entityId, DCL.Models.CLASS_ID classId,
 400            Vector3 position, string remoteSrc = "")
 401        {
 402            CreateSceneEntity(scene, entityId);
 403
 404            if (string.IsNullOrEmpty(remoteSrc))
 405            {
 406                CreateAndSetShape(scene, entityId, classId, "{}");
 407            }
 408            else
 409            {
 410                CreateAndSetShape(scene, entityId, classId, JsonConvert.SerializeObject(new
 411                {
 412                    src = remoteSrc
 413                }));
 414            }
 415
 416            SetEntityTransform(scene, scene.entities[entityId], position, Quaternion.identity, Vector3.one);
 417        }
 418
 419        public static void DetachSharedComponent(ParcelScene scene, string fromEntityId, string sharedComponentId)
 420        {
 421            if (!scene.entities.TryGetValue(fromEntityId, out IDCLEntity entity))
 422            {
 423                return;
 424            }
 425
 426            scene.GetSharedComponent(sharedComponentId).DetachFrom(entity);
 427        }
 428
 429        public static void InstantiateEntityWithMaterial(ParcelScene scene, string entityId, Vector3 position,
 430            BasicMaterial.Model basicMaterial, string materialComponentID = "a-material")
 431        {
 432            InstantiateEntityWithShape(scene, entityId, DCL.Models.CLASS_ID.BOX_SHAPE, position);
 433
 434            scene.SharedComponentCreate(
 435                materialComponentID,
 436                (int) DCL.Models.CLASS_ID.BASIC_MATERIAL
 437            );
 438
 439            scene.SharedComponentUpdate(
 440                materialComponentID,
 441                JsonUtility.ToJson(basicMaterial));
 442
 443            scene.SharedComponentAttach(
 444                entityId,
 445                materialComponentID
 446            );
 447        }
 448
 449        public static void InstantiateEntityWithMaterial(ParcelScene scene, string entityId, Vector3 position,
 450            PBRMaterial.Model pbrMaterial, string materialComponentID = "a-material")
 451        {
 452            InstantiateEntityWithShape(scene, entityId, DCL.Models.CLASS_ID.BOX_SHAPE, position);
 453
 454            scene.SharedComponentCreate(
 455                materialComponentID,
 456                (int) CLASS_ID.PBR_MATERIAL
 457            );
 458
 459            scene.SharedComponentUpdate(
 460                materialComponentID,
 461                JsonUtility.ToJson(pbrMaterial));
 462
 463            scene.SharedComponentAttach(
 464                entityId,
 465                materialComponentID
 466            );
 467        }
 468
 469        public static IEnumerator CreateAudioSource(ParcelScene scene, string entityId, string audioClipId, bool playing
 470        {
 471            var audioSourceModel = new DCLAudioSource.Model()
 472            {
 473                audioClipId = audioClipId,
 474                playing = playing,
 475                volume = 1.0f,
 476                loop = loop,
 477                pitch = 1.0f
 478            };
 479
 480            DCLAudioSource audioSource =
 481                TestHelpers.EntityComponentCreate<DCLAudioSource, DCLAudioSource.Model>(scene, scene.entities[entityId],
 482                    audioSourceModel);
 483
 484            yield return audioSource.routine;
 485        }
 486
 487        public static IEnumerator LoadAudioClip(ParcelScene scene, string audioClipId, string url, bool loop, bool loadi
 488            float volume, bool waitForLoading = true)
 489        {
 490            DCLAudioClip.Model model = new DCLAudioClip.Model
 491            {
 492                url = url,
 493                loop = loop,
 494                shouldTryToLoad = loading,
 495                volume = volume
 496            };
 497
 498            DCLAudioClip audioClip = scene.SharedComponentCreate(
 499                audioClipId,
 500                (int) CLASS_ID.AUDIO_CLIP
 501            ) as DCLAudioClip;
 502
 503            scene.SharedComponentUpdate(audioClipId, JsonUtility.ToJson(model));
 504
 505            yield return audioClip.routine;
 506
 507            Assert.IsTrue(scene.disposableComponents.ContainsKey(audioClipId), "Shared component was not created correct
 508
 509            if (waitForLoading)
 510            {
 511                yield return new WaitUntil(
 512                    () =>
 513                    {
 514                        return audioClip.loadingState != DCLAudioClip.LoadState.LOADING_IN_PROGRESS &&
 515                               audioClip.loadingState != DCLAudioClip.LoadState.IDLE;
 516                    });
 517            }
 518        }
 519
 520        public static IEnumerator CreateAudioSourceWithClipForEntity(IDCLEntity entity)
 521        {
 522            yield return LoadAudioClip(entity.scene as ParcelScene,
 523                audioClipId: "audioClipTest",
 524                url: TestAssetsUtils.GetPath() + "/Audio/Train.wav",
 525                loop: true,
 526                loading: true,
 527                volume: 1f,
 528                waitForLoading: true);
 529
 530            yield return CreateAudioSource(entity.scene as ParcelScene,
 531                entityId: entity.entityId,
 532                audioClipId: "audioClipTest",
 533                playing: true);
 534        }
 535
 536        public static string GetComponentUniqueId(ParcelScene scene, string salt, int classId, string entityId)
 537        {
 538            string baseId = salt + "-" + (int) classId + "-" + entityId;
 539            string finalId = baseId;
 540
 541            while (scene.GetSharedComponent(finalId) != null)
 542            {
 543                finalId = baseId + UnityEngine.Random.Range(1, 10000);
 544            }
 545
 546            return finalId;
 547        }
 548
 549        public static string CreateAndSetShape(ParcelScene scene, string entityId, CLASS_ID classId, string model)
 550        {
 551            string componentId = GetComponentUniqueId(scene, "shape", (int) classId, entityId);
 552
 553            scene.SharedComponentCreate(
 554                componentId,
 555                (int) classId
 556            );
 557
 558            scene.SharedComponentUpdate(
 559                componentId,
 560                model);
 561
 562            scene.SharedComponentAttach(
 563                entityId,
 564                componentId
 565            );
 566
 567            return componentId;
 568        }
 569
 570        public static void UpdateShape(ParcelScene scene, string componentId, string model) { scene.SharedComponentUpdat
 571
 572        static object GetRandomValueForType(System.Type t)
 573        {
 574            if (t == typeof(float))
 575            {
 576                return 79014.5f;
 577            }
 578            else if (t == typeof(int))
 579            {
 580                return 79014;
 581            }
 582            else if (t == typeof(string))
 583            {
 584                return "test";
 585            }
 586            else if (t == typeof(Color))
 587            {
 588                return Color.magenta;
 589            }
 590            else if (t == typeof(object))
 591            {
 592                return Activator.CreateInstance(t);
 593            }
 594
 595            return null;
 596        }
 597
 598        public static void CompareWithDefaultedInstance<TModel, TComponent>(TComponent component)
 599            where TModel : class, new()
 600            where TComponent : IComponent
 601        {
 602            MemberInfo modelMember = null;
 603
 604            modelMember = typeof(TComponent).GetRuntimeProperties().FirstOrDefault((x) => x.Name == "model");
 605
 606            if (modelMember == null)
 607            {
 608                modelMember = typeof(TComponent).GetRuntimeFields().FirstOrDefault((x) => x.Name == "model");
 609            }
 610
 611            Assert.IsTrue(modelMember != null, "model is null!!");
 612            TModel defaultedModel = new TModel();
 613
 614            object tmpModel = null;
 615
 616            //NOTE(Brian): Get model object
 617            if (modelMember is FieldInfo)
 618            {
 619                tmpModel = (modelMember as FieldInfo).GetValue(component);
 620            }
 621            else if (modelMember is PropertyInfo)
 622            {
 623                tmpModel = (modelMember as PropertyInfo).GetValue(component);
 624            }
 625
 626            TModel model = tmpModel as TModel;
 627
 628            Assert.IsTrue(model != null, "Model is null, there's a type mismatch between TModel type and the actual mode
 629
 630            foreach (FieldInfo f in typeof(TModel).GetFields())
 631            {
 632                System.Type t = f.GetType();
 633
 634                //NOTE(Brian): Get model field
 635                object defaultValue = f.GetValue(defaultedModel);
 636                object modelValue = f.GetValue(model);
 637                string fieldName = f.Name;
 638
 639                //NOTE(Brian): Corner case, strings are defaulted as null, but json deserialization inits them as ""
 640                if (modelValue is string && string.IsNullOrEmpty(modelValue as string))
 641                {
 642                    modelValue = null;
 643                }
 644
 645                //NOTE(Brian): Corner case, arrays are defaulted as null, but json deserialization inits them as an empt
 646                if (modelValue is Array)
 647                {
 648                    modelValue = null;
 649                }
 650
 651                Assert.AreEqual(defaultValue, modelValue, $"Checking {fieldName} failed! Is not default value! error!");
 652            }
 653        }
 654
 655        public static IEnumerator TestEntityComponentDefaultsOnUpdate<TModel, TComponent>(ParcelScene scene)
 656            where TComponent : BaseComponent
 657            where TModel : class, new()
 658        {
 659            TModel generatedModel = new TModel();
 660
 661            foreach (FieldInfo f in typeof(TModel).GetFields())
 662            {
 663                System.Type t = f.FieldType;
 664                object valueToSet = GetRandomValueForType(t);
 665                f.SetValue(generatedModel, valueToSet);
 666            }
 667
 668            IDCLEntity e = CreateSceneEntity(scene);
 669            TComponent component = EntityComponentCreate<TComponent, TModel>(scene, e, generatedModel);
 670
 671            if (component.routine != null)
 672            {
 673                yield return component.routine;
 674            }
 675
 676            var factory = Environment.i.world.componentFactory as RuntimeComponentFactory;
 677            IPoolableComponentFactory poolableFactory = factory.poolableComponentFactory;
 678            int id = (int) poolableFactory.GetIdForType<TComponent>();
 679
 680            scene.EntityComponentUpdate(e, (CLASS_ID_COMPONENT) id, "{}");
 681
 682            if (component.routine != null)
 683            {
 684                yield return component.routine;
 685            }
 686
 687            CompareWithDefaultedInstance<TModel, TComponent>(component);
 688            TestHelpers.RemoveSceneEntity(scene, e.entityId);
 689        }
 690
 691        public static IEnumerator TestAttachedSharedComponentOfSameTypeIsReplaced<TModel, TComponent>(ParcelScene scene,
 692            CLASS_ID classId)
 693            where TComponent : BaseDisposable
 694            where TModel : class, new()
 695        {
 696            // Create scene entity and 1st component
 697            IDCLEntity entity = CreateSceneEntity(scene);
 698
 699            var component = SharedComponentCreate<TComponent, TModel>(scene, classId);
 700
 701            if (component.routine != null)
 702            {
 703                yield return component.routine;
 704            }
 705
 706            System.Type componentType = typeof(TComponent);
 707
 708            if (component is BaseShape)
 709            {
 710                componentType = typeof(BaseShape);
 711            }
 712
 713            // Attach 1st component to entity
 714            TestHelpers.SharedComponentAttach(component, entity);
 715
 716            Assert.IsTrue(entity.GetSharedComponent(componentType) != null);
 717            Assert.AreEqual(component, entity.GetSharedComponent(componentType));
 718
 719            // Assign 2nd component to same entity
 720            var component2 = SharedComponentCreate<TComponent, TModel>(scene, classId);
 721
 722            if (component2.routine != null)
 723            {
 724                yield return component2.routine;
 725            }
 726
 727            TestHelpers.SharedComponentAttach(component2, entity);
 728
 729            Assert.IsTrue(entity.GetSharedComponent(componentType) != null);
 730            Assert.AreEqual(component2, entity.GetSharedComponent(componentType));
 731            Assert.IsFalse(component.attachedEntities.Contains(entity));
 732        }
 733
 734        public static IEnumerator TestSharedComponentDefaultsOnUpdate<TModel, TComponent>(ParcelScene scene,
 735            CLASS_ID id)
 736            where TComponent : BaseDisposable
 737            where TModel : class, new()
 738        {
 739            TComponent component = TestHelpers.SharedComponentCreate<TComponent, TModel>(scene, id);
 740
 741            if (component.routine != null)
 742            {
 743                yield return component.routine;
 744            }
 745
 746            TModel generatedModel = new TModel();
 747
 748            foreach (FieldInfo f in typeof(TModel).GetFields())
 749            {
 750                System.Type t = f.FieldType;
 751                object valueToSet = GetRandomValueForType(t);
 752                f.SetValue(generatedModel, valueToSet);
 753            }
 754
 755            yield return SharedComponentUpdate(component, generatedModel);
 756
 757            yield return TestHelpers.SharedComponentUpdate(component, new TModel());
 758
 759            yield return component.routine;
 760
 761            CompareWithDefaultedInstance<TModel, TComponent>(component);
 762
 763            component.Dispose();
 764        }
 765
 766        public static IEnumerator TestShapeCollision(BaseShape shapeComponent, BaseShape.Model shapeModel, IDCLEntity en
 767        {
 768            var scene = shapeComponent.scene;
 769
 770            // make sure the shape is collidable first
 771            shapeModel.withCollisions = true;
 772            SharedComponentUpdate(shapeComponent, shapeModel);
 773            yield return shapeComponent.routine;
 774
 775            // check every collider is enabled
 776            Assert.IsTrue(entity.meshesInfo.colliders.Count > 0);
 777
 778            for (int i = 0; i < entity.meshesInfo.colliders.Count; i++)
 779            {
 780                Assert.IsTrue(entity.meshesInfo.colliders[i].enabled);
 781            }
 782
 783            // update collision property with 'false'
 784            shapeModel.withCollisions = false;
 785            SharedComponentUpdate(shapeComponent, shapeModel);
 786            yield return shapeComponent.routine;
 787
 788            // check colliders correct behaviour
 789            for (int i = 0; i < entity.meshesInfo.colliders.Count; i++)
 790            {
 791                Assert.IsFalse(entity.meshesInfo.colliders[i].enabled);
 792            }
 793
 794            // update collision property with 'true' again
 795            shapeModel.withCollisions = true;
 796            SharedComponentUpdate(shapeComponent, shapeModel);
 797            yield return shapeComponent.routine;
 798
 799            // check colliders correct behaviour
 800            for (int i = 0; i < entity.meshesInfo.colliders.Count; i++)
 801            {
 802                Assert.IsTrue(entity.meshesInfo.colliders[i].enabled);
 803            }
 804        }
 805
 806        public static IEnumerator TestShapeVisibility(BaseShape shapeComponent, BaseShape.Model shapeModel, IDCLEntity e
 807        {
 808            // make sure the shape is visible first
 809            shapeModel.visible = true;
 810            yield return SharedComponentUpdate(shapeComponent, shapeModel);
 811
 812            // check every mesh is shown by default
 813            Renderer[] renderers = entity.meshesInfo.renderers;
 814
 815            Assert.IsTrue(renderers.Length > 0);
 816
 817            for (int i = 0; i < renderers.Length; i++)
 818            {
 819                Assert.IsTrue(renderers[i].enabled);
 820            }
 821
 822            yield return TestShapeOnPointerEventCollider(entity);
 823
 824            // update visibility with 'false'
 825            shapeModel.visible = false;
 826            yield return SharedComponentUpdate(shapeComponent, shapeModel);
 827
 828            // check renderers correct behaviour
 829            for (int i = 0; i < renderers.Length; i++)
 830            {
 831                Assert.IsFalse(renderers[i].enabled);
 832            }
 833
 834            yield return TestShapeOnPointerEventCollider(entity);
 835
 836            // update visibility with 'true'
 837            shapeModel.visible = true;
 838            yield return SharedComponentUpdate(shapeComponent, shapeModel);
 839
 840            // check renderers correct behaviour
 841            for (int i = 0; i < renderers.Length; i++)
 842            {
 843                Assert.IsTrue(renderers[i].enabled);
 844            }
 845
 846            yield return TestShapeOnPointerEventCollider(entity);
 847        }
 848
 849        public static IEnumerator TestShapeOnPointerEventCollider(IDCLEntity entity)
 850        {
 851            Renderer[] renderers = entity.meshesInfo.renderers;
 852
 853            Assert.IsTrue(renderers.Length > 0);
 854
 855            var onClickComponentModel = new OnClick.Model()
 856            {
 857                type = OnClick.NAME,
 858                uuid = "onClick"
 859            };
 860
 861            ParcelScene scene = entity.scene as ParcelScene;
 862
 863            var onClickComponent = TestHelpers.EntityComponentCreate<OnClick, OnClick.Model>(scene, entity, onClickCompo
 864            yield return onClickComponent.routine;
 865
 866            Collider onPointerEventCollider;
 867            for (int i = 0; i < renderers.Length; i++)
 868            {
 869                Assert.IsTrue(renderers[i].transform.childCount > 0, "OnClick collider should exist as this mesh's child
 870
 871                onPointerEventCollider = renderers[i].transform.GetChild(0).GetComponent<Collider>();
 872                Assert.IsTrue(onPointerEventCollider != null);
 873                Assert.IsTrue(onPointerEventCollider.gameObject.layer == PhysicsLayers.onPointerEventLayer);
 874
 875                // check the onClick collide enabled state is the same as the renderer's state
 876                Assert.IsTrue(onPointerEventCollider.enabled == renderers[i].enabled);
 877            }
 878
 879            scene.EntityComponentRemove(
 880                entity.entityId,
 881                onClickComponent.name
 882            );
 883            yield return null;
 884        }
 885
 886        public static IEnumerator TestUIElementAddedCorrectlyOnInvisibleParent<TComponent, TComponentModel>(ParcelScene 
 887            where TComponent : UIShape
 888            where TComponentModel : UIShape.Model, new()
 889        {
 890            UIScreenSpace parentElement = TestHelpers.SharedComponentCreate<UIScreenSpace, UIScreenSpace.Model>(scene, C
 891            yield return parentElement.routine;
 892
 893            // make canvas invisible
 894            yield return SharedComponentUpdate(parentElement, new UIScreenSpace.Model { visible = false });
 895
 896            TComponent targetUIElement =
 897                SharedComponentCreate<TComponent, TComponentModel>(scene,
 898                    classId,
 899                    new TComponentModel
 900                    {
 901                        parentComponent = parentElement.id,
 902                        width = new UIValue(100f),
 903                        height = new UIValue(100f)
 904                    });
 905            yield return targetUIElement.routine;
 906
 907            RectTransform uiCanvasRectTransform = parentElement.childHookRectTransform.GetComponentInParent<RectTransfor
 908            Assert.AreEqual(uiCanvasRectTransform.rect.width / 2, targetUIElement.referencesContainer.layoutElementRT.an
 909            Assert.AreEqual(-uiCanvasRectTransform.rect.height / 2, targetUIElement.referencesContainer.layoutElementRT.
 910
 911            Assert.AreEqual(100f, targetUIElement.referencesContainer.layoutElementRT.rect.width);
 912            Assert.AreEqual(100f, targetUIElement.referencesContainer.layoutElementRT.rect.height);
 913        }
 914
 915        public static IEnumerator TestUIClickEventPropagation(string sceneId, UIShape.Model model, RectTransform uiObjec
 916        {
 917            string srcOnClick = model.onClick;
 918            bool srcIsPointerBlocker = model.isPointerBlocker;
 919
 920            model.isPointerBlocker = true;
 921            model.onClick = "UUIDFakeEventId";
 922
 923            yield return TestUIClickEventPropagation(sceneId, model.onClick, uiObject, callback);
 924
 925            model.isPointerBlocker = srcIsPointerBlocker;
 926            model.onClick = srcOnClick;
 927
 928            yield return null;
 929        }
 930
 931        public static IEnumerator TestUIClickEventPropagation(string sceneId, string eventUuid, RectTransform uiObject, 
 932        {
 933            // We need to populate the event data with the 'pointerPressRaycast' pointing to the 'clicked' object
 934            PointerEventData pointerEventData = new PointerEventData(EventSystem.current);
 935            RaycastResult raycastResult = new RaycastResult();
 936            raycastResult.gameObject = uiObject.gameObject;
 937            pointerEventData.pointerPressRaycast = raycastResult;
 938
 939            string targetEventType = "SceneEvent";
 940
 941            var onClickEvent = new WebInterface.OnClickEvent();
 942            onClickEvent.uuid = eventUuid;
 943
 944            var sceneEvent = new WebInterface.SceneEvent<WebInterface.OnClickEvent>();
 945            sceneEvent.sceneId = sceneId;
 946            sceneEvent.payload = onClickEvent;
 947            sceneEvent.eventType = "uuidEvent";
 948            string eventJSON = JsonUtility.ToJson(sceneEvent);
 949
 950            bool eventTriggered = false;
 951
 952            yield return TestHelpers.WaitForMessageFromEngine(targetEventType, eventJSON,
 953                () =>
 954                {
 955                    ExecuteEvents.ExecuteHierarchy(raycastResult.gameObject, pointerEventData,
 956                        ExecuteEvents.pointerDownHandler);
 957                },
 958                () => { eventTriggered = true; });
 959
 960            yield return null;
 961
 962            // Callback!
 963            if (callback != null)
 964                callback(eventTriggered);
 965        }
 966
 967        public static IEnumerator TestUIOnPointerDownEventPropagation(string sceneId, string eventUuid, RectTransform ui
 968        {
 969            // We need to populate the event data with the 'pointerPressRaycast' pointing to the 'clicked' object
 970            PointerEventData pointerEventData = new PointerEventData(EventSystem.current);
 971            RaycastResult raycastResult = new RaycastResult();
 972            raycastResult.gameObject = uiObject.gameObject;
 973            pointerEventData.pointerPressRaycast = raycastResult;
 974
 975            string targetEventType = "SceneEvent";
 976
 977            var onPointerDownEvent = new WebInterface.OnPointerDownEvent();
 978            onPointerDownEvent.uuid = eventUuid;
 979
 980            var sceneEvent = new WebInterface.SceneEvent<WebInterface.OnPointerDownEvent>();
 981            sceneEvent.sceneId = sceneId;
 982            sceneEvent.payload = onPointerDownEvent;
 983            sceneEvent.eventType = "uuidEvent";
 984            string eventJSON = JsonUtility.ToJson(sceneEvent);
 985
 986            bool eventTriggered = false;
 987
 988            yield return TestHelpers.WaitForMessageFromEngine(targetEventType, eventJSON,
 989                () =>
 990                {
 991                    ExecuteEvents.ExecuteHierarchy(raycastResult.gameObject, pointerEventData,
 992                        ExecuteEvents.pointerDownHandler);
 993                },
 994                () => { eventTriggered = true; });
 995
 996            yield return null;
 997
 998            // Callback!
 999            if (callback != null)
 1000                callback(eventTriggered);
 1001        }
 1002
 1003        public static IEnumerator TestUIOnPointerUpEventPropagation(string sceneId, string eventUuid, RectTransform uiOb
 1004        {
 1005            // We need to populate the event data with the 'pointerPressRaycast' pointing to the 'clicked' object
 1006            PointerEventData pointerEventData = new PointerEventData(EventSystem.current);
 1007            RaycastResult raycastResult = new RaycastResult();
 1008            raycastResult.gameObject = uiObject.gameObject;
 1009            pointerEventData.pointerPressRaycast = raycastResult;
 1010
 1011            string targetEventType = "SceneEvent";
 1012
 1013            var onPointerUpEvent = new WebInterface.OnPointerUpEvent();
 1014            onPointerUpEvent.uuid = eventUuid;
 1015
 1016            var sceneEvent = new WebInterface.SceneEvent<WebInterface.OnPointerUpEvent>();
 1017            sceneEvent.sceneId = sceneId;
 1018            sceneEvent.payload = onPointerUpEvent;
 1019            sceneEvent.eventType = "uuidEvent";
 1020            string eventJSON = JsonUtility.ToJson(sceneEvent);
 1021
 1022            bool eventTriggered = false;
 1023
 1024            yield return TestHelpers.WaitForMessageFromEngine(targetEventType, eventJSON,
 1025                () =>
 1026                {
 1027                    ExecuteEvents.ExecuteHierarchy(raycastResult.gameObject, pointerEventData,
 1028                        ExecuteEvents.pointerDownHandler);
 1029                },
 1030                () => { eventTriggered = true; });
 1031
 1032            yield return null;
 1033
 1034            // Callback!
 1035            if (callback != null)
 1036                callback(eventTriggered);
 1037        }
 1038
 1039        // Simulates a mouse click by throwing a ray over a given object and checks
 1040        // if that object is the first one that the ray pass through
 1041        public static bool TestUIClick(Canvas canvas, RectTransform rectT)
 1042        {
 1043            PointerEventData pointerEventData = new PointerEventData(EventSystem.current);
 1044
 1045            Vector2 imagePos = rectT.position;
 1046            float scale = canvas.scaleFactor;
 1047            Vector2 pos = imagePos;
 1048
 1049            pos *= scale;
 1050            pos.x *= ((RectTransform) canvas.transform).sizeDelta.x / Screen.width;
 1051            pos.y *= ((RectTransform) canvas.transform).sizeDelta.y / Screen.height;
 1052
 1053            pointerEventData.position = pos;
 1054
 1055            List<RaycastResult> results = new List<RaycastResult>();
 1056            EventSystem.current.RaycastAll(pointerEventData, results);
 1057
 1058            // Check that there's at least one result
 1059            if (results.Count == 0)
 1060            {
 1061                return false;
 1062            }
 1063
 1064            // Check that the clicked object is the one on the front
 1065            return results[0].gameObject == rectT.gameObject;
 1066        }
 1067
 1068        public static IEnumerator UnloadAllUnityScenes()
 1069        {
 1070            if (SceneManager.sceneCount == 1)
 1071                yield break;
 1072
 1073            for (int i = SceneManager.sceneCount - 1; i >= 0; i--)
 1074            {
 1075                var scene = SceneManager.GetSceneAt(i);
 1076                yield return SceneManager.UnloadSceneAsync(scene);
 1077            }
 1078        }
 1079
 1080        public static IEnumerator WaitForMessageFromEngine(string targetMessageType, string targetMessageJSONPayload,
 1081            System.Action OnIterationStart, System.Action OnSuccess)
 1082        {
 1083            string lastMessageFromEngineType = "";
 1084            string lastMessageFromEnginePayload = "";
 1085
 1086            bool awaitedConditionMet = false;
 1087
 1088            Action<string, string> msgFromEngineCallback = (eventType, eventPayload) =>
 1089            {
 1090                lastMessageFromEngineType = eventType;
 1091                lastMessageFromEnginePayload = eventPayload;
 1092
 1093                if (lastMessageFromEngineType == targetMessageType &&
 1094                    lastMessageFromEnginePayload == targetMessageJSONPayload)
 1095                {
 1096                    OnSuccess?.Invoke();
 1097                    awaitedConditionMet = true;
 1098                }
 1099            };
 1100
 1101            // Hook up to web interface engine message reporting
 1102            WebInterface.OnMessageFromEngine += msgFromEngineCallback;
 1103
 1104            yield return new DCL.WaitUntil(() =>
 1105            {
 1106                OnIterationStart?.Invoke();
 1107                return awaitedConditionMet;
 1108            }, 2f);
 1109
 1110            WebInterface.OnMessageFromEngine -= msgFromEngineCallback;
 1111        }
 1112
 1113        /// <summary>
 1114        /// This method intercepts a message being sent to kernel and calls OnSuccess
 1115        /// When the message matches the targetMessageType
 1116        ///
 1117        /// CAUTION: Do not use this to assert a message is NOT being sent to kernel,
 1118        /// the enumerator will yield until the timeout (2 seconds) have passed.
 1119        /// </summary>
 1120        /// <param name="targetMessageType">
 1121        /// The target message type to be intercepted.
 1122        /// </param>
 1123        /// <param name="evt">
 1124        /// Reference event useful for template inference.
 1125        /// </param>
 1126        /// <param name="OnIterationStart">
 1127        /// This callback will kept being called until the message is intercepted or
 1128        /// a timeout occurs. Useful for raising events.
 1129        /// </param>
 1130        /// <param name="OnMessageReceived">
 1131        /// This func will be invoked with any matching message as raw json.
 1132        /// If the func returns true, the execution will end.
 1133        /// </param>
 1134        /// <returns>IEnumerator to be yielded</returns>
 1135        public static IEnumerator ExpectMessageToKernel<T>(
 1136            string targetMessageType,
 1137            T evt,
 1138            Action OnIterationStart,
 1139            Func<T, bool> OnMessageReceived = null)
 1140        {
 1141            bool messageWasReceived = false;
 1142
 1143            void MsgFromEngineCallback(string eventType, string eventPayload)
 1144            {
 1145                string lastMessageFromEngineType = eventType;
 1146                string lastMessageFromEnginePayload = eventPayload;
 1147
 1148                if (string.IsNullOrEmpty(lastMessageFromEnginePayload))
 1149                    return;
 1150
 1151                if (lastMessageFromEngineType != targetMessageType)
 1152                    return;
 1153
 1154                if (OnMessageReceived == null)
 1155                {
 1156                    messageWasReceived = true;
 1157                    return;
 1158                }
 1159
 1160                messageWasReceived = OnMessageReceived.Invoke(JsonUtility.FromJson<T>(lastMessageFromEnginePayload));
 1161            }
 1162
 1163            // Hook up to web interface engine message reporting
 1164            WebInterface.OnMessageFromEngine += MsgFromEngineCallback;
 1165
 1166            yield return new DCL.WaitUntil(() =>
 1167            {
 1168                OnIterationStart?.Invoke();
 1169                return messageWasReceived;
 1170            }, 2f);
 1171
 1172            WebInterface.OnMessageFromEngine -= MsgFromEngineCallback;
 1173        }
 1174
 1175        public static void TestRectTransformMaxStretched(RectTransform rt)
 1176        {
 1177            Assert.AreEqual(Vector2.zero, rt.anchorMin,
 1178                $"Rect transform {rt.name} isn't stretched out!. unexpected anchorMin value.");
 1179            Assert.AreEqual(Vector2.zero, rt.offsetMin,
 1180                $"Rect transform {rt.name} isn't stretched out!. unexpected offsetMin value.");
 1181            Assert.AreEqual(Vector2.one, rt.anchorMax,
 1182                $"Rect transform {rt.name} isn't stretched out!. unexpected anchorMax value.");
 1183            Assert.AreEqual(Vector2.one, rt.offsetMax,
 1184                $"Rect transform {rt.name} isn't stretched out!. unexpected offsetMax value.");
 1185            Assert.AreEqual(Vector2.zero, rt.sizeDelta,
 1186                $"Rect transform {rt.name} isn't stretched out!. unexpected sizeDelta value.");
 1187        }
 1188
 1189        public static void SetCharacterPosition(Vector3 newPosition) { DCLCharacterController.i.Teleport(JsonConvert.Ser
 1190
 1191        public static IEnumerator WaitForGLTFLoad(IDCLEntity entity)
 1192        {
 1193            LoadWrapper_GLTF wrapper = GLTFShape.GetLoaderForEntity(entity) as LoadWrapper_GLTF;
 1194            return new WaitUntil(() => wrapper.alreadyLoaded);
 1195        }
 1196    }
 1197}

Methods/Properties

keepWaiting()