Example #1
0
        public override void Set()
        {
            if (!AutomationMaster.AllMethodsInFramework.Any())
            {
                AutomationMaster.SetAllMethodsInFramework();
            }

            string        rawFavoritesData = FileBroker.GetNonUnityTextResource(AutomationMaster.UnitTestMode ? FileResource.FavoritesUnit : FileResource.Favorites);
            List <string> favoritesEachRaw = rawFavoritesData.Split(AutomationMaster.DELIMITER).ToList();

            FavoritesList = new List <KeyValuePair <string, List <KeyValuePair <bool, string> > > >();
            for (int f = 0; f < favoritesEachRaw.Count; f++)
            {
                //Invalid data
                if (!favoritesEachRaw[f].Trim().Contains(internalDelimiter))
                {
                    continue;
                }

                List <string> pieces = favoritesEachRaw[f].Split(new string[] { internalDelimiter }, StringSplitOptions.None).ToList();
                pieces = pieces.RemoveNullAndEmpty();                 //Remove possible empties due to line breaks at end of file.
                string name = pieces.First();
                List <KeyValuePair <bool, string> > tests = new List <KeyValuePair <bool, string> >();
                for (int t = 1; t < pieces.Count; t++)
                {
                    tests.Add(new KeyValuePair <bool, string>(pieces[t].EndsWith(classIndicator) ? true : false, pieces[t].EndsWith(classIndicator) ? pieces[t].Replace(classIndicator, string.Empty) : pieces[t]));
                }
                FavoritesList.Add(new KeyValuePair <string, List <KeyValuePair <bool, string> > >(name, tests));
            }
        }
Example #2
0
		//Record logs from Unity console.
		public static void GetLog(string message, string stackTrace, LogType type) {

			Log newLog = new Log() {
				message = message,
				stackTrace = stackTrace,
				type = type,
			};
			Logs.Add(newLog);
			//If we exceed the maximum storage space.
			if(Logs.Count >= MAX_LOG_COUNT_HISTORY) {
				
				Logs.RemoveAt(0); //Remove oldest log.

			}

			//If an unhandled exception has occurred in the Trilleon Framework during a test run, handle it!
			if(type == LogType.Exception) {

				#if UNITY_EDITOR
				EditorApplication.UnlockReloadAssemblies();
				#endif

				//If this error occurred directly in the Trilleon Framework (not editor), then stop all tests and report on the exception.
				if(AutomationMaster.Busy &&  AutomationMaster.Initialized && stackTrace.Contains("TrilleonAutomation.") && !stackTrace.Contains("/Editor/") && !ReportOnce) {

					ReportOnce = true;
					AutomationMaster.Arbiter.SendCommunication("An unhandled exception occurred in the Trilleon Framework, disrupting the test run execution");
					string stack = string.Format("The unhandled exception was: {0} {1}", message, stackTrace);
					if(stack.Length > ConnectionStrategy.MaxMessageLength) {
						
						stack = stack.Substring(0, ConnectionStrategy.MaxMessageLength - 50);

					}
					AutomationMaster.Arbiter.SendCommunication(stack);
					string assertionData = string.Join("**", AutomationMaster.CurrentTestContext.Assertions.ToArray());
					if(assertionData.Length > ConnectionStrategy.MaxMessageLength) {

						int startingIndex = assertionData.Length - ConnectionStrategy.MaxMessageLength - 50;
						assertionData = stack.Substring(startingIndex, stack.Length - startingIndex - 1);

					}
					AutomationMaster.Arbiter.SendCommunication(string.Format("ASSERTION DATA:[{0}]", assertionData));
					AutomationMaster.AutomationReport.ReportUnhandledException(message, stackTrace);

					#if UNITY_EDITOR
					//Reset test runner
					GameObject helper = GameObject.Find(TestMonitorHelpers.NAME);
					if(helper != null)
						AutomationMaster.Destroy(helper);
					#endif

					AutomationMaster.Destroy(AutomationMaster.StaticSelf);
					AutomationMaster.Initialize();
					AutomationMaster.StaticSelfComponent.ResetTestRunner();
					AutoConsole.PostMessage("Exception in framework killed TestRunner. Framework reset and ready for new commands.", MessageLevel.Abridged);

				} else if(AutomationMaster.Busy && AutomationMaster.Initialized && !stackTrace.Contains("/Editor/")) {

					AutomationMaster.TestRunContext.Exceptions.Add(newLog);

				}

				#if UNITY_EDITOR
				if(!AutomationMaster.Initialized && stackTrace.Contains("TrilleonAutomation.") && message.ToLower().Contains("object reference")) {

					//Without AutomationMaster.Initialize(), a null reference error will occur trying to use most functionality in the Trilleon framework.
					string missingRefError = "Object reference error in Trilleon framework without initialization. This is most often caused when TrilleonAutomation.Initialize() is not called. This method is required in your Game's startup logic to activate Trilleon.";
					Debug.LogError(missingRefError);
					AutoConsole.PostMessage(missingRefError, MessageLevel.Abridged);

				}
				#endif
				if(ConnectionStrategy.TrilleonConnectionStrategy == ConnectionStrategyType.Socket) {

					AutomationMaster.StaticSelf.GetComponent<SocketConnectionStrategy>().Stop();

				}

			}

		}
Example #3
0
        public static void GetMostRecentsResults(string status = "", string plainText = "")
        {
            _testsMeta = new List <KeyValuePair <string, string[]> >();

            //TODO: Remove tests from file that no longer are automation tests.
            bool skipCreate = string.IsNullOrEmpty(status);

            List <string> resultsRaw = new List <string>();

            string txt = FileBroker.GetNonUnityTextResource(FileResource.LatestTestResults);

            resultsRaw = txt.Split(new string[] { "\n" }, StringSplitOptions.None).ToList();

            if (resultsRaw.Count == 0 && !skipCreate)
            {
                fileText.AppendLine(plainText);
            }
            else
            {
                List <Type>       currentClasses = AutomationMaster.GetAutomationClasses();
                List <MethodInfo> currentMethods = new List <MethodInfo>();
                currentClasses.ForEach(x => currentMethods.AddRange(x.GetMethods()));

                //Filter out tests that no longer exist, have been renamed, or are no longer marked as automation methods.
                currentMethods = currentMethods.FindAll(x => {
                    Automation[] aut = (Automation[])Attribute.GetCustomAttributes(x, typeof(Automation));
                    return(aut != null && aut.Length > 0);
                });

                for (int i = 0; i < resultsRaw.Count; i++)
                {
                    if (!string.IsNullOrEmpty(resultsRaw[i]))
                    {
                        string[] row      = resultsRaw[i].Split(AutomationMaster.DELIMITER);
                        string   testName = string.Empty;
                        for (int r = 0; r < row.Length; r++)
                        {
                            string[] keyVal = row[r].Split(':');
                            if (keyVal[0] == "name")
                            {
                                testName = keyVal[1];
                            }
                        }
                        if (!string.IsNullOrEmpty(testName))
                        {
                            //Ignore references to methods that are no longer valid automation methods for whatever reason.
                            if (currentMethods.FindAll(x => x.Name == testName).Any())
                            {
                                _testsMeta.Add(new KeyValuePair <string, string[]>(testName, row));
                            }
                        }
                    }
                }

                if (!skipCreate)
                {
                    List <KeyValuePair <string, string[]> > matches = testsMeta.FindAll(x => x.Key == AutomationMaster.CurrentTestContext.TestName);

                    if (!matches.Any())
                    {
                        _testsMeta.Add(new KeyValuePair <string, string[]>(AutomationMaster.CurrentTestContext.TestName, plainText.Split(AutomationMaster.DELIMITER)));
                    }
                    else
                    {
                        _testsMeta.Remove(matches.First());
                        string[] newValues = new string[] {
                            string.Format("status:{0}", status),
                            string.Format("name:{0}", AutomationMaster.CurrentTestContext.TestName),
                            string.Format("class:{0}", AutomationMaster.CurrentTestContext.ClassName),
                            string.Format("test_categories:{0}", string.Join(",", AutomationMaster.CurrentTestContext.Categories.ToArray())),
                        };
                        KeyValuePair <string, string[]> newInsert = new KeyValuePair <string, string[]>(AutomationMaster.CurrentTestContext.TestName, newValues);
                        _testsMeta.Add(newInsert);
                    }

                    for (int f = 0; f < testsMeta.Count; f++)
                    {
                        fileText.AppendLine(string.Join(AutomationMaster.DELIMITER.ToString(), _testsMeta[f].Value));
                    }
                }
            }
        }
Example #4
0
        void GetDependencyClassTestStructure()
        {
            //Get all automation test methods.
            List <KeyValuePair <Type, MethodInfo> > allTestMethods = new List <KeyValuePair <Type, MethodInfo> >();
            List <Type> AutomationClasses           = AutomationMaster.GetAutomationClasses();
            List <Type> masterDependencyClasses     = AutomationClasses.FindAll(x => x.GetCustomAttributes(typeof(DependencyClass), false).ToList().Any());
            List <Type> masterlessDependencyClasses = AutomationClasses.FindAll(x => {
                return(!x.GetCustomAttributes(typeof(DependencyClass), false).ToList().Any() && x.GetMethods().ToList().FindAll(y => {
                    DependencyTest dt = (DependencyTest)Attribute.GetCustomAttribute(y, typeof(DependencyTest));
                    return dt != null;
                }).Any());
            });

            for (int i = 0; i < AutomationClasses.Count; i++)
            {
                List <MethodInfo> methods = AutomationClasses[i].GetMethods().ToList().FindAll(y => y.GetCustomAttributes(typeof(Automation), false).ToList().Any());
                for (int x = 0; x < methods.Count; x++)
                {
                    allTestMethods.Add(new KeyValuePair <Type, MethodInfo>(AutomationClasses[i], methods[x]));
                }
            }

            //Add groupings of master dependencies (DependencyTests under DependencyClasses)
            int classId = 0;

            for (int a = 0; a < masterDependencyClasses.Count; a++)
            {
                List <KeyValuePair <Type, MethodInfo> > allMasterDependencyMethods = new List <KeyValuePair <Type, MethodInfo> >();
                for (int all = 0; all < masterDependencyClasses.Count; all++)
                {
                    List <MethodInfo> allMethods = masterDependencyClasses[all].GetMethods().ToList().FindAll(y => y.GetCustomAttributes(typeof(DependencyTest), false).ToList().Any());
                    for (int am = 0; am < allMethods.Count; am++)
                    {
                        allMasterDependencyMethods.Add(new KeyValuePair <Type, MethodInfo>(masterDependencyClasses[all], allMethods[am]));
                    }
                }

                List <MethodInfo> thisDependencyClassIdMethods = allMasterDependencyMethods.FindAll(x => {
                    //Return all methods under the current DependencyClass ID.
                    DependencyClass dc = (DependencyClass)Attribute.GetCustomAttribute(x.Key, typeof(DependencyClass));
                    return(dc != null && dc.order == classId);
                }).ExtractListOfValuesFromKeyValList();

                thisDependencyClassIdMethods = thisDependencyClassIdMethods.FindAll(x => {
                    //Return all methods that have a DependencyTest attribute.
                    DependencyTest dt = (DependencyTest)Attribute.GetCustomAttribute(x, typeof(DependencyTest));
                    return(dt != null);
                });

                //If any methods found.
                if (thisDependencyClassIdMethods.Any())
                {
                    KeyValuePair <int, string> DepClassThis = new KeyValuePair <int, string>(classId, string.Empty);                 //This Dep Class id.
                    List <KeyValuePair <Type, MethodInfo> > DepTestThese = new List <KeyValuePair <Type, MethodInfo> >();

                    for (int ms = 0; ms < thisDependencyClassIdMethods.Count; ms++)
                    {
                        List <MethodInfo> nextMethod = thisDependencyClassIdMethods.FindAll(x => {
                            DependencyTest dt = (DependencyTest)Attribute.GetCustomAttribute(x, typeof(DependencyTest));
                            return(dt.order == ms + 1);
                        });

                        if (nextMethod.Any())
                        {
                            //Add method to next order in list.
                            DepTestThese.Add(new KeyValuePair <Type, MethodInfo>(allMasterDependencyMethods.FindAll(x => x.Value.Name == nextMethod.First().Name).First().Key, nextMethod.First()));
                        }
                        else
                        {
                            int duplicateId = 0;
                            List <KeyValuePair <string, int> > ordersFound = new List <KeyValuePair <string, int> >();
                            //Check if there is a duplicate ID, rather than a missing ID.
                            for (int d = 0; d < thisDependencyClassIdMethods.Count; d++)
                            {
                                DependencyTest dt = (DependencyTest)Attribute.GetCustomAttribute(thisDependencyClassIdMethods[d], typeof(DependencyTest));
                                if (ordersFound.FindAll(x => x.Value == dt.order).Any())
                                {
                                    duplicateId = dt.order;
                                    ordersFound.Add(new KeyValuePair <string, int>(thisDependencyClassIdMethods[d].Name, dt.order));
                                    break;
                                }
                                else
                                {
                                    ordersFound.Add(new KeyValuePair <string, int>(thisDependencyClassIdMethods[d].Name, dt.order));
                                }
                            }

                            List <Type> allTestClassesThatShareThisMasterId = allMasterDependencyMethods.FindAll(x => {
                                DependencyClass dc = (DependencyClass)Attribute.GetCustomAttribute(x.Key, typeof(DependencyClass));
                                return(dc.order == DepClassThis.Key);
                            }).ExtractListOfKeysFromKeyValList().Distinct();
                            StringBuilder classNameList = new StringBuilder();
                            for (int nl = 0; nl < allTestClassesThatShareThisMasterId.Count; nl++)
                            {
                                classNameList.Append(allTestClassesThatShareThisMasterId[nl].Name);
                                if (nl + 1 != allTestClassesThatShareThisMasterId.Count)
                                {
                                    classNameList.Append(", ");
                                }
                            }

                            if (duplicateId > 0)
                            {
                                StringBuilder testNameList = new StringBuilder();
                                for (int nl = 0; nl < ordersFound.Count; nl++)
                                {
                                    testNameList.Append(ordersFound[nl].Key);
                                    if (nl + 1 != ordersFound.Count)
                                    {
                                        testNameList.Append(", ");
                                    }
                                }
                                _errorInDependencyUsage = true;
                                throw new UnityException(string.Format("There multiple tests with the DependencyTest ID of {0} under the DependencyClass ( Name(s): {1} - DependencyClass ID: {2} ). Tests with duplicate ID ( {3} )", duplicateId, classNameList.ToString(), DepClassThis.Key, testNameList.ToString()));
                            }
                            else
                            {
                                _errorInDependencyUsage = true;
                                throw new UnityException(string.Format("There should be a DependencyTest of ID {0} under the DependencyClass ( Name(s): {1} - ID: {2} )", ms + 1, classNameList.ToString(), DepClassThis.Key));
                            }
                        }
                    }

                    //Add this pairing to the building list of all Dependencies.
                    DependencyOrderingMaster.Add(new KeyValuePair <KeyValuePair <int, string>, List <KeyValuePair <Type, MethodInfo> > >(DepClassThis, DepTestThese));
                }
                else
                {
                    break;
                }

                classId++;
            }

            //Add groupings of master dependencies (DependencyTests under DependencyClasses)
            for (int b = 0; b < masterlessDependencyClasses.Count; b++)
            {
                List <KeyValuePair <Type, MethodInfo> > DepTestThese = new List <KeyValuePair <Type, MethodInfo> >();
                KeyValuePair <int, string> DepClassThis = new KeyValuePair <int, string>(0, masterlessDependencyClasses[b].Name);             //This Dep Class id.

                //Add each method based on the id of the Dependency Test.
                List <MethodInfo> allMasterlessDependencyMethods = masterlessDependencyClasses[b].GetMethods().ToList().FindAll(y => y.GetCustomAttributes(typeof(DependencyTest), false).ToList().Any());
                for (int ms = 0; ms < allMasterlessDependencyMethods.Count; ms++)
                {
                    MethodInfo nextMethod = allMasterlessDependencyMethods.FindAll(x => {
                        DependencyTest dc = (DependencyTest)Attribute.GetCustomAttribute(x, typeof(DependencyTest));
                        return(dc.order == ms + 1);
                    }).First();
                    //Add method to next order in list.
                    DepTestThese.Add(new KeyValuePair <Type, MethodInfo>(masterlessDependencyClasses[b], nextMethod));
                }

                //Add this pairing to the building list of all Dependencies.
                DependencyOrderingMasterless.Add(new KeyValuePair <KeyValuePair <int, string>, List <KeyValuePair <Type, MethodInfo> > >(DepClassThis, DepTestThese));
            }

            //Filter debug classes, unless debug classes represent ALL existing dependency archituecture tests. In that case, show debug tests to demonstrate how this editor window works.
            List <KeyValuePair <KeyValuePair <int, string>, List <KeyValuePair <Type, MethodInfo> > > > filteredDebugMaster = DependencyOrderingMaster.FindAll(x => !x.Value.FindAll(t => t.Key.GetCustomAttributes(typeof(DebugClass), false).Length > 0).Any());
            List <KeyValuePair <KeyValuePair <int, string>, List <KeyValuePair <Type, MethodInfo> > > > filteredDebugMasterfilteredDebugMaster = DependencyOrderingMasterless.FindAll(x => !x.Value.FindAll(t => t.Key.GetCustomAttributes(typeof(DebugClass), false).Length > 0).Any());

            if (filteredDebugMaster.Any() || filteredDebugMasterfilteredDebugMaster.Any())
            {
                DependencyOrderingMaster     = filteredDebugMaster;
                DependencyOrderingMasterless = filteredDebugMasterfilteredDebugMaster;
            }
        }
Example #5
0
        //DependencyWebs consists of a Key (the id of the web list) and a Value (a list of KeyValuePairs which consist of a DependencyWeb method and the method that this declares as a dependency.
        void MapDependencies()
        {
            //Get all automation test methods.
            List <KeyValuePair <string, MethodInfo> > allTestMethods = new List <KeyValuePair <string, MethodInfo> >();
            List <Type> AutomationClasses = AutomationMaster.GetAutomationClasses().FindAll(x => lastPassDetectedNoWebTestsSoDisplayDebugAsExample || !x.GetCustomAttributes(false).OfType <DebugClass>().Any());           //Ignore debug classes.

            for (int i = 0; i < AutomationClasses.Count(); i++)
            {
                List <MethodInfo> methods = AutomationClasses[i].GetMethods().Where(y => y.GetCustomAttributes(false).OfType <Automation>().Any()).ToList();
                for (int x = 0; x < methods.Count(); x++)
                {
                    allTestMethods.Add(new KeyValuePair <string, MethodInfo>(methods[x].Name, methods[x]));
                }
            }

            //From all methods, get methods that declare dependencies.
            List <KeyValuePair <string, MethodInfo> > allDependencyTests = allTestMethods.Where(x => {
                return(x.Value.GetCustomAttributes(typeof(DependencyWeb), false).Any());
            }).ToList();

            testsAndTheirDependenciesList = new List <KeyValuePair <string, string[]> >();

            if (allDependencyTests.Count == 0)
            {
                lastPassDetectedNoWebTestsSoDisplayDebugAsExample = true;                 //Show DependencyTests marked as Debug, as a demo for what this window shows when a user has their own DependencyTests.
                return;
            }

            //Get list of every test name associated with its declared dependencies.
            for (int i = 0; i < allDependencyTests.Count; i++)
            {
                DependencyWeb dw      = (DependencyWeb)Attribute.GetCustomAttribute(allDependencyTests[i].Value, typeof(DependencyWeb));
                List <string> dtNames = dw.Dependencies;
                dtNames.AddRange(dw.OneOfDependencies);
                testsAndTheirDependenciesList.Add(new KeyValuePair <string, string[]>(allDependencyTests[i].Key, dtNames.ToArray()));
            }

            //List of all methods.
            List <string> allInvolvedTestMethods = new List <string>();

            //Build dependency web connections.
            for (int t = 0; t < testsAndTheirDependenciesList.Count; t++)
            {
                allInvolvedTestMethods.Add(testsAndTheirDependenciesList[t].Key);
                allInvolvedTestMethods.AddRange(testsAndTheirDependenciesList[t].Value);
            }
            allInvolvedTestMethods = allInvolvedTestMethods.Distinct().ToList();

            for (int all = 0; all < allInvolvedTestMethods.Count; all++)
            {
                string         method = allInvolvedTestMethods[all];
                DependencyNode node   = new DependencyNode();
                node.TestName = method;

                List <KeyValuePair <string, string[]> > matchChild = testsAndTheirDependenciesList.Where(x => x.Key == method).ToList();
                if (matchChild.Any())
                {
                    List <string> children = matchChild.Single().Value.ToList();
                    for (int c = 0; c < children.Count; c++)
                    {
                        node.AddDependency(DependencyNodeConnectionType.Outgoing, children[c]);
                    }
                }

                List <string> parents = testsAndTheirDependenciesList.Where(x => x.Value.Contains(method)).Select(x => x.Key).ToList();
                if (parents.Any())
                {
                    for (int p = 0; p < parents.Count; p++)
                    {
                        node.AddDependency(DependencyNodeConnectionType.Incoming, parents[p]);
                    }
                }

                DependencyWebs.Add(node);
            }
        }
Example #6
0
        protected IEnumerator Unifier(bool b, bool inverse, string message, FailureContext newFailureContext, params int[] testRailsId)
        {
            //If test was already marked as a failure, and test has flag indicating that it should continue despite failure, ignore.
            if (!AutomationMaster.CurrentTestContext.IsSuccess || ((AutomationMaster.TryContinueOnFailure || MideExecution_MarkTestToTryContinueAfterFail) && IsFailing))
            {
                UnitTestStepFailure = isSoft = isTry = quiet = false;                 //Reset the UnitTestStepFailure, Soft, Try, and Quiet flags.
                yield break;
            }

            //Automatically label this assertion as quiet if the previous assertion is identical to this one.
            quiet = quiet ? true : AutomationMaster.CurrentTestContext.Assertions.Last() == message;

            _failureContext = newFailureContext;
            if ((!b && inverse) || (b && !inverse))
            {
                if (isTry && !quiet)
                {
                    AutomationMaster.CurrentTestContext.AddAssertion(string.Format("**TRY_SUCCESS**{0}", message));
                    UnitTestStepFailure = isSoft = isTry = quiet = false;                     //Reset the UnitTestStepFailure, Soft, Try, and Quiet flags.
                    yield break;
                }

                ConcurrentFailures = 0;
                AutomationMaster.CurrentTestContext.IsSuccess = true;
                if (!string.IsNullOrEmpty(message) && !isTry && !quiet)
                {
                    AutomationMaster.CurrentTestContext.AddAssertion(message);
                }
            }
            else
            {
                //TODO: UnitTestStepFailure - Determine if an assertion has failed within the context of a TestObject "steps" method. If so, set this to true. Used to disabled certain TestRunner reactive logic, such as screenshots.

                if (isTry)
                {
                    if (!quiet)
                    {
                        AutomationMaster.CurrentTestContext.AddAssertion(string.Format("**TRY_FAIL**{0}", message));
                    }
                    UnitTestStepFailure = isSoft = isTry = quiet = false;                     //Reset the UnitTestStepFailure, Soft, Try, and Quiet flags.
                    yield break;
                }

                IsFailing = true;
                bool recordLogDetails = newFailureContext != FailureContext.Skipped;

                SetReflectedTestData();
                string recentLogs = AutomationReport.EncodeCharactersForJson(AutoConsole.ReturnLatestLogs(5));

                if (newFailureContext == FailureContext.Skipped)
                {
                    AutomationMaster.TestRunContext.Skipped.Add(AutomationMaster.CurrentTestContext.TestName);
                }
                else
                {
                    AutomationMaster.TestRunContext.Failed.Add(AutomationMaster.CurrentTestContext.TestName, new string[] {
                        message,
                        recentLogs.ToString(),
                        lineNumber
                    });
                }

                AutomationMaster.CurrentTestContext.IsSuccess = false;
                AutomationMaster.CurrentTestContext.AddAssertion(message);
                AutomationMaster.CurrentTestContext.ErrorDetails += string.Format("Error Message [{0}] : Test Line [{1}] : Debug Logs [{2}] ", message, string.Format("Line [{0}] Call [{1}]", lineNumber, lineCall), (recordLogDetails ? recentLogs : string.Format("#SKIPPED#{0}", message)));
                AutomationMaster.CurrentTestContext.ErrorDetails += string.Format(" FULL STACK: [{0}]", Environment.StackTrace.Replace(" at", string.Format(" {0} at", AutomationMaster.NEW_LINE_INDICATOR)));
                if (failureContext != FailureContext.Skipped)
                {
                    //Take screenshot if a failure is not a "Skip" failure (In which case a test does not run at all, and there is no value in taking a screenshot as the current screen has no relevance to the reason it failed).
                    yield return(StartCoroutine(AutomationMaster.StaticSelfComponent.TakeScreenshot()));

                    screenshotRequestTime = DateTime.UtcNow;
                }

                //Handle errors occurring outside of the context of the current test's execution. Only certain contexts require additional handling over what is offered by default.
                switch (AutomationMaster.ExecutionContext)
                {
                case AutomationMaster.CurrentExecutionContext.SetUpClass:
                    AutomationMaster.AutoSkips.Add(new KeyValuePair <string[], string>(new string[] { "class", AutomationMaster.CurrentTestContext.ClassName }, string.Format("FAILURE OCCURRED IN SETUPCLASS:", message)));
                    break;

                case AutomationMaster.CurrentExecutionContext.SetUp:
                    AutomationMaster.AutoSkips.Add(new KeyValuePair <string[], string>(new string[] { "test", AutomationMaster.CurrentTestContext.TestName }, string.Format("FAILURE OCCURRED IN SETUP:", message)));
                    break;

                case AutomationMaster.CurrentExecutionContext.TearDownClass:
                    yield return(StartCoroutine(Q.assert.Warn(string.Format("A failure occurred in the TearDownClass logic for  the test \"{0}.{1}\". This fails the last-run test, and may cause other undesirable behavior for downstream test execution.", AutomationMaster.CurrentTestContext.ClassName, AutomationMaster.CurrentTestContext.TestName))));

                    //Will automatically handle the failure of this test.
                    break;

                case AutomationMaster.CurrentExecutionContext.TearDown:
                //Will automatically handle the failure of this test.
                case AutomationMaster.CurrentExecutionContext.Test:
                //Will automatically handle the failure of this test.
                default:
                    break;
                }

                if ((AutomationMaster.TryContinueOnFailure || MideExecution_MarkTestToTryContinueAfterFail) && ConcurrentFailures > 5)
                {
                    AutomationMaster.OverrideContinueOnFailureAfterTooManyConcurrentFailures = true;
                }

                                #if UNITY_EDITOR
                AutomationMaster.PauseEditorOnFailure();
                                #endif

                //Any FailureContext beyond TestMethod will not have an instantiated test method.
                if (!AutomationMaster.TryContinueOnFailure)
                {
                    if ((!isSoft && AutomationMaster.OverrideContinueOnFailureAfterTooManyConcurrentFailures) || (!MideExecution_MarkTestToTryContinueAfterFail && (_failureContext == FailureContext.TestMethod || _failureContext == FailureContext.Default) && failureContext != FailureContext.Skipped))
                    {
                        try {
                            AutomationMaster.CurrentTestMethod.Stop();                //Kill current test, only if the currently queued test has been initialized.
                        } catch { }
                        yield return(new WaitForEndOfFrame());                        //Allow all Coroutines to be stopped before returning control. In reality, the coroutine calling this will be stopped, so control will never be returned anyway.
                    }
                }

                if (!isSoft && (AutomationMaster.TryContinueOnFailure || MideExecution_MarkTestToTryContinueAfterFail))
                {
                    ConcurrentFailures++;
                }
            }

            if (testRailsId.Length > 0)
            {
                AutomationReport.MarkTestRailsTestCase(AutomationMaster.CurrentTestContext.IsSuccess ? "Passed" : "Failed", testRailsId);
            }

            AutoConsole.PostMessage(string.Format("Assert [{0}] |{1}| {2}", AutomationMaster.CurrentTestContext.TestName, AutomationMaster.CurrentTestContext.IsSuccess ? "Success" : "Failure", message), MessageLevel.Verbose, ConsoleMessageType.TestRunnerUpdate);
            UnitTestStepFailure = isSoft = isTry = quiet = false;             //Reset the UnitTestStepFailure, Soft, Try, and Quiet flags.
            yield return(null);
        }
Example #7
0
        /// <summary>
        /// Handles incoming pubsub messages. Expects JSON format.
        /// </summary>
        public IEnumerator HandleMessage(string result)
        {
            //Ignore duplicate or empty messages. Ignore messages not meant for this client.
            if (lastMessageReceived == result || string.IsNullOrEmpty(result.Trim()))
            {
                yield break;
            }

            lastMessageReceived = result;

            List <KeyValuePair <string, string> > parameters = DeserializeJsonString(result);

            if (!LocalRunLaunch)
            {
                //If no context or identity is provided, then this is not a valid command. If the DeviceUdid is not valid, then ignore the command.
                if (!parameters.FindAll(x => x.Key.ToLower() == "grid_source").Any() || !parameters.FindAll(x => x.Key.ToLower() == "grid_identity").Any())
                {
                    yield break;
                }

                string source   = parameters.Find(x => x.Key == "grid_source").Value;
                string identity = parameters.Find(x => x.Key == "grid_identity").Value;
                string buddy    = parameters.Find(x => x.Key == "grid_identity_buddy").Value;

                //If message simply contains no reference to this GridIdentity OR the identity is self, then it is chatter and can be ignored.
                bool isChatter = !result.Contains(GridIdentity);
                bool isInvalid = string.IsNullOrEmpty(TestRunId) ? !parameters.FindAll(x => x.Key == "set_test_run_id").Any() : parameters.FindAll(x => x.Key == "test_run_id").Any() && TestRunId != parameters.Find(x => x.Key == "test_run_id").Value;
                bool isEcho    = identity == GridIdentity && source == "client";

                //If message is from a client source where the identity is not that of this client, but contains this client's identity, then this it is a BuddySystem message.
                bool isBuddyMessage = source != "server" && parameters.FindAll(x => x.Key.StartsWith("buddy_")).Any() && buddy == GridIdentity && identity == BuddyHandler.BuddyName;

                //If this message is meant for a different client, or is an echo from the current client, simply ignore the message.
                if (!isBuddyMessage && (isChatter || isEcho || isInvalid))
                {
                    yield break;
                }
            }
            else if (!LocalRunLaunch && parameters.FindAll(x => x.Key.ToLower() == "grid_identity").Any() ? parameters.Find(x => x.Key == "grid_identity").Value != GridIdentity : false)
            {
                yield break;
            }

            LastMessage = DateTime.Now;

            if (parameters.Count > 0)
            {
                //Process each command.
                for (int c = 0; c < parameters.Count; c++)
                {
                    string command             = parameters[c].Key.ToLower();
                    string message             = parameters[c].Value.TrimEnd(',');
                    bool   isRecognizedCommand = true;

                    switch (command.ToLower())
                    {
                    case "change_connection_strategy":
                        AutomationMaster.ConnectionStrategy.ChangeConnectionStrategy(message);
                        break;

                    case "change_communications_identity":
                        AutomationMaster.ConnectionStrategy.UpdateChannelIdentity(message);
                        break;

                    case "no_interval_screenshots":
                        AutomationMaster.NoIntervalScreenshots = true;
                        break;

                    case "ignore_memory_tracking":
                        AutomationMaster.IgnoreMemoryTracking = true;
                        break;

                    case "health_check":
                        SendCommunication(string.Format("heartbeat_{0}", (++AutomationMaster.HeartBeatIndex).ToString(), "0"));
                        break;

                    case "buddy_ignore_all":
                        AutomationMaster.IgnoreAllBuddyTests      = true;
                        AutomationMaster.LockIgnoreBuddyTestsFlag = true;     //Prevents Test editor window from implicitly updating the Ignore flag.
                        break;

                    case "buddy_ready_for_tests":
                        //TODO: Refactor and re-add GRIDLOCK logic. Without it, BuddySystem will not informatively report that both buddies are reporting as the same role.
                        //if((BuddyHandler.IsPrimary && message == "primary") || (!BuddyHandler.IsPrimary && message == "secondary")) {

                        //Gridlock. One client must be the primary, and one must be the secondary.
                        //SendCommunication("buddy_gridlock_detected", "0");
                        //BuddyHandler.RoleGridLock = true;

                        //} else {

                        SendCommunication("buddy_ready_for_tests_acknowledged", BuddyHandler.IsPrimary ? "primary" : "secondary");
                        BuddyHandler.IsBuddyReadyForBuddyTests = true;

                        //}
                        break;

                    case "buddy_ready_for_tests_acknowledged":
                        BuddyHandler.HasBuddyAcknowledgedOurReadiness = true;
                        break;

                    case "buddy_switching_roles":
                        BuddyHandler.BuddyHasSuccessfullySwitchRoles = true;
                        break;

                    case "buddy_requesting_required_details":
                        //Send/Resend details required by Primary Buddy.
                        BuddyHandler.SendBasicBuddyDetails();
                        break;

                    case "buddy_starting_reaction":
                        BuddyHandler.SecondaryReactionsStarted = true;
                        break;

                    case "buddy_tearing_down":
                        BuddyHandler.BuddyTearingDown = true;
                        break;

                    case "buddy_data_update":
                        BuddyHandler.SetCurrentBuddyRequiredDetails(message);
                        break;

                    case "buddy_primary_test_complete":
                        BuddyHandler.CurrentPrimaryTest    = message;
                        BuddyHandler.ReadyForReactionTests = true;
                        BuddyHandler.SendBuddyCommunication("buddy_xyz", string.Format("Buddy Primary Test Completion ({0}) Acknowledged ({1}) %%%%", BuddyHandler.CurrentPrimaryTest, BuddyHandler.ReadyForReactionTests));
                        break;

                    case "buddy_primary_pretest_commands":
                        AutomationMaster.BuddyHandler.PreTestCommandReceived(message);
                        break;

                    case "buddy_secondary_pretest_commands_complete":
                        BuddyHandler.BuddyProcessingCommands = false;
                        break;

                    case "buddy_secondary_pretest_commands_failure":
                        BuddyHandler.BuddyCommandExecutionFailure = true;
                        BuddyHandler.BuddyProcessingCommands      = false;
                        BuddyHandler.BuddyCommandFailure          = message;
                        break;

                    case "buddy_secondary_tests_complete":
                        BuddyHandler.WaitingForBuddyToCompleteReactionTests = false;
                        break;

                    case "buddy_primary_test_failed":
                        BuddyHandler.PrimaryFailed = true;
                        break;

                    case "buddy_primary_complete_action_tests":
                        BuddyHandler.IsPrimaryFinishedWithActionTests = true;
                        break;

                    case "loop_tests":
                        //This command should be sent before or at the same time as the run command. Sending it after may result in failing to have the desired effect.
                        List <KeyValuePair <string, int> > loopTests = new List <KeyValuePair <string, int> >();
                        List <string> RawRequests = message.Split(AutomationMaster.DELIMITER).ToList();
                        for (int x = 0; x < RawRequests.Count; x++)
                        {
                            string testName = RawRequests[x].Split('@').First();
                            string count    = RawRequests[x].Split('@').Last();
                            if (RawRequests[x].Split('@').ToList().Count != 2 || count.ToInt() == 0)
                            {
                                AutoConsole.PostMessage("Provided loop_tests command is invalid. The value must be a string and then integer, separated by an @ symbol.");
                                continue;
                            }
                            loopTests.Add(new KeyValuePair <string, int>(testName, count.ToInt()));
                        }
                        AutomationMaster.LoopTests = loopTests;
                        break;

                    case "request_response":
                        switch (message)
                        {
                        case "screenshot":
                            AutomationMaster.AwaitingScreenshot = false;
                            break;

                        default:
                            break;
                        }
                        break;

                    case "request_buddy":
                        //AutomationMaster.BuddyRequest(message, "newbuddy");
                        break;

                    case "set_test_run_id":
                        TestRunId = message;
                        break;

                    case "manual_set_buddy_primary":
                        BuddyHandler.BuddyName = message;
                        BuddyHandler.IsPrimary = true;
                        BuddyHandler.SendBasicBuddyDetails();
                        BuddyIdentity = message;
                        break;

                    case "manual_set_buddy_secondary":
                        BuddyHandler.BuddyName = message;
                        BuddyHandler.IsPrimary = false;
                        BuddyHandler.SendBasicBuddyDetails();
                        BuddyIdentity = message;
                        break;

                    case "no_test_rails_reporting":
                        AutomationReport.IgnoreTestRailsReporting = true;
                        break;

                    case "server_heartbeat":
                        AutomationMaster.ServerHeartbeatReceived();
                        break;

                    case "console_command":
                        List <string> commands = message.Trim().Split('|').ToList();
                        for (int co = 0; co < commands.Count; co++)
                        {
                            string com = string.Format("{0} {1}", commands[co].Split('$').First(), commands[co].Split('$').Last());
                            Q.SendConsoleCommand(com);
                            AutoConsole.PostMessage(string.Format("Ran Command: {0}", com), MessageLevel.Abridged);
                        }
                        break;

                    case "server_broker_response":
                        Q.request.CommandResponseReceived(message);
                        break;

                    case "automation_command":
                        if (AutomationMaster.Busy)
                        {
                            SendCommunication("Notification", "Busy completing previous test run.");
                            break;
                        }
                        SendCommunication("Notification", "Beginning pre-run checks.");

                        if (parameters.Find(x => x.Key == "grid_source").Value == "server")
                        {
                            AutomationMaster.IsServerListening = true;
                        }

                        //Incoming server-based runs will require the carrot before "rt all", for example, to run unit tests instead of automation.
                        if (message.StartsWith("^"))
                        {
                            AutomationMaster.UnitTestMode = true;
                        }

                        yield return(StartCoroutine(Q.driver.WaitRealTime(1)));

                        AutomationMaster.Busy = true;
                        AutomationMaster.LockIgnoreBuddyTestsFlag = true;

                        //Split string and discard only command prefix. Also allows for spaces in test Category names.
                        message = message.TrimStart().TrimEnd().Replace(", ", ",").Split(new char[] { ' ' }, 2)[1].Trim().ToLower();
                        if (message == "all")
                        {
                            AutomationMaster.LaunchType = LaunchType.All;
                        }
                        else if (message.StartsWith("*") && message.Contains(","))
                        {
                            message = message.Replace("*", string.Empty);
                            AutomationMaster.LaunchType = LaunchType.MultipleMethodNames;
                        }
                        else if (message.StartsWith("*"))
                        {
                            message = message.Replace("*", string.Empty);
                            AutomationMaster.LaunchType = LaunchType.MethodName;
                        }
                        else if (message.StartsWith("&&"))
                        {
                            message = message.Replace("&&", string.Empty);
                            AutomationMaster.LaunchType = LaunchType.Mix;
                        }
                        else if (message.Contains(","))
                        {
                            AutomationMaster.LaunchType = LaunchType.MultipleCategoryNames;
                        }
                        else
                        {
                            AutomationMaster.LaunchType = LaunchType.CategoryName;
                        }

                        //Wait until loading of game is complete to attempt a launch of the automation suite
                        yield return(StartCoroutine(Q.game.WaitForGameLoadingComplete()));

                        StartCoroutine(AutomationMaster.StaticSelfComponent.BeginTestLaunch(message));

                        break;

                    case "buddy_secondary_test_complete":
                    case "buddy_requesting_value_ready":
                    case "buddy_setting_ready_to":
                        //Commands that do not require any action, but should be considered valid for logging purposes (isRecognizedCommand).
                        break;

                    default:
                        isRecognizedCommand = false;
                        break;
                    }

                    Arbiter.LocalRunLaunch = false;
                    if (isRecognizedCommand && !string.IsNullOrEmpty(message))
                    {
                        AutoConsole.PostMessage(string.Format("SENDER [{0}] - COMMAND [{1}] - MESSAGE [{2}]", parameters.Find(x => x.Key == "grid_identity").Value, command, message), ConsoleMessageType.Pubsub);
                    }
                }
            }
        }