public static string GetTextResource(FileResource file, string extendableResourceName = "") { if(!isSet) { Set(); } if(!knownTrilleonResourceFiles.FindAll(x => x.Key == file).Any()) { AutoConsole.PostMessage("The supplied file is not an expected Trilleon resource. Make sure you are referencing a file declared in the FileBroker.cs Set() logic."); //No usage of string.Format with file name data. Using Enum.GetName here causes exceptions on compile "Recursive Serialization is not supported. You can't dereference a PPtr while loading.". return string.Empty; } TextAsset txt = (TextAsset)Resources.Load(knownTrilleonResourceFiles.Find(x => x.Key == file && (file == FileResource.ExtendableResource ? x.Value == extendableResourceName : true)).Value, typeof(TextAsset)); if(txt != null) { return txt.text; } return string.Empty; }
/// <summary> /// Returns the text resource by the resource enumeration name, or by the provided string name if it is an extendable resource. /// </summary> public static string GetNonUnityTextResource(FileResource file, string extendableResourceName = "") { if(!isSet) { Set(); } if(!knownEditorResourceFiles.FindAll(x => x.Key == file && (file == FileResource.ExtendableResource ? x.Value == extendableResourceName : true)).Any()) { AutoConsole.PostMessage("The supplied file is not an expected Trilleon resource. Make sure you are referencing a file declared in the FileBroker.cs Set() logic."); //No usage of string.Format with file name data. Using Enum.GetName here causes exceptions on compile "Recursive Serialization is not supported. You can't dereference a PPtr while loading.". return string.Empty; } string directory = string.Format("{0}{1}{2}", RESOURCES_DIRECTORY, FILE_PATH_SPLIT, knownEditorResourceFiles.Find(x => x.Key == file).Value); string fileText = string.Empty; if(!Exists(directory)) { Set(); } fileText = File.ReadAllText(directory); return fileText; }
public IEnumerator SendRequest(string endpoint, RequestType requestType, List <KeyValuePair <string, string> > args, List <KeyValuePair <string, string> > headers) { WWWForm form = new WWWForm(); for (int x = 0; x < args.Count; x++) { form.AddField(args[x].Key, args[x].Value); } Dictionary <string, string> _headers = form.headers; for (int x = 0; x < headers.Count; x++) { _headers[headers[x].Key] = headers[x].Value; } string url = Endpoints.Find(x => x.Key == endpoint).Value; WWW www = new WWW(url, form.data, _headers); yield return(www); string message = string.Empty; if (www.error == null) { message = string.Format("API Call Success: {0}", url); } else { message = string.Format("API Call Failure: {0} Message: {1}", url, www.error); yield return(StartCoroutine(Q.assert.Fail(message))); } AutoConsole.PostMessage(message); yield return(null); }
public static void SaveTextResource(FileResource file, string value) { if(!isSet) { Set(); } #if UNITY_EDITOR if(!knownTrilleonResourceFiles.FindAll(x => x.Key == file).Any()) { AutoConsole.PostMessage("The supplied file could not be found. Make sure you are referencing a file stored outside of the Unity project."); //No usage of string.Format with file name data. Using Enum.GetName here causes exceptions on compile "Recursive Serialization is not supported. You can't dereference a PPtr while loading.". return; } string filePath = string.Format("{0}{1}{2}.txt", Application.dataPath.Replace("/", FILE_PATH_SPLIT), AutomationMaster.ConfigReader.GetString("UNITY_RESOURCES_FILE_PATH").Replace("/", FILE_PATH_SPLIT), knownTrilleonResourceFiles.Find(x => x.Key == file).Value); File.WriteAllText(filePath, value); AssetDatabase.Refresh(); if(file == FileResource.TrilleonConfig) { AutomationMaster.ConfigReader.Refresh(); } #endif }
void SendCommunicationActual(List <KeyValuePair <string, string> > parameters) { //Generally no need to communicate with a server if we are in the Editor. Else, just comment out. if (!Application.isEditor || AutomationMaster.ConfigReader.GetBool("SEND_COMMUNICATIONS_IN_EDITOR")) { StringBuilder json = new StringBuilder(); json.Append("["); //--Required Identifiers-- json.Append(string.Format("{{\"grid_identity\":\"{0}\"}},", GridIdentity)); json.Append(string.Format("{{\"grid_identity_buddy\":\"{0}\"}},", BuddyHandler.BuddyName)); json.Append("{{\"grid_source\":\"client\"}},"); json.Append(string.Format("{{\"test_run_id\":\"{0}\"}},", TestRunId)); json.Append(string.Format("{{\"device_udid\":\"{0}\"}},", DeviceUdid)); json.Append(string.Format("{{\"test_run_time\":\"{0}\"}},", AutomationMaster.Busy ? AutomationReport.TestSuiteRunTime.ToString() : "0 ")); json.Append(string.Format("{{\"game\":\"{0}\"}},", GameMaster.GAME_NAME)); //--End Required Params-- for (int x = 0; x < parameters.Count; x++) { json.Append(string.Format("{{\"{0}\":\"{1}\"}}", parameters[x].Key.ToLower(), parameters[x].Value.Replace("\"", "@APOS@").Replace("'", "@QUOT@"))); if (x + 1 < parameters.Count) { json.Append(","); } } json.Append("]"); ConnectionStrategy.SendCommunication(json.ToString()); AutoConsole.PostMessage(json.ToString(), ConsoleMessageType.Pubsub); } }
public static void SendJsonInPieces(string jsonAttributePrefix, string json) { //Break this message into pieces. List <string> pieces = new List <string>(); int charPos = 0; while (charPos < json.Length) { if (charPos + ConnectionStrategy.MaxMessageLength <= json.Length) { pieces.Add(json.Substring(charPos, ConnectionStrategy.MaxMessageLength)); charPos += ConnectionStrategy.MaxMessageLength; } else { pieces.Add(json.Substring(charPos, json.Length - charPos - 1)); break; } } for (int x = 0; x < pieces.Count; x++) { AutoConsole.PostMessage(string.Format("{0}_MULTI_PART|{1}{2}{3}|", jsonAttributePrefix, x, AutomationMaster.PARTIAL_DELIMITER, pieces[x]), MessageLevel.Abridged); } }
/// <summary> /// Grant current account all requested values. /// </summary> /// <param name="grant">Grant.</param> public IEnumerator GrantPlayer(Grant grant) { for (int i = 0; i < ALL_FIELDS.Length; i++) { switch (ALL_FIELDS[i]) { case "Gold": if (grant.Gold >= 0) { AutoConsole.PostMessage(string.Format("Granting Gold: {0}", grant.Gold)); yield return(StartCoroutine(Grant("GRANT GOLD", grant.Gold))); yield return(StartCoroutine(Q.driver.WaitRealTime(4))); } break; case "Silver": if (grant.Silver >= 0) { AutoConsole.PostMessage(string.Format("Granting Silver: {0}", grant.Silver)); yield return(StartCoroutine(Grant("GRANT SILVER", grant.Silver))); yield return(StartCoroutine(Q.driver.WaitRealTime(4))); } break; default: throw new UnityException("Grant Command not recognized."); } } }
public void Add(string test) { if (!_tests.Contains(test)) { Tests.Add(test); string message = string.Format("[{0}] {1}", test, "#Ignore#"); AutoConsole.PostMessage(message); } }
static string ResetTestRailsTestStatuses(List <string> args) { if (args.First().ToInt() > 0) { TestRailsAPIClient client = new TestRailsAPIClient(GameMaster.BASE_TEST_RAILS_URL); if (client == null) { string message = "COULD NOT RESET RAILS! New API Client could not be instantiated."; AutoConsole.PostMessage(message, MessageLevel.Abridged); return(message); } string json = client.SendGet(string.Format("get_tests/{0}", args.First())); System.Object jsonObj = Json.Deserialize(json); List <System.Object> list = (List <System.Object>)jsonObj; StringBuilder jsonUpdates = new StringBuilder(); jsonUpdates.Append("{\"results\": ["); for (int i = 0; i < list.Count; i++) { Dictionary <string, object> item = (Dictionary <string, object>)list[i]; int id = item["id"].ToString().ToInt(); int statusId = item["status_id"].ToString().ToInt(); //If test is not marked as Blocked. if (statusId != 2) { jsonUpdates.AppendLine("{"); jsonUpdates.Append(string.Format("\"test_id\":{0},", id)); jsonUpdates.Append("\"status_id\":4,"); jsonUpdates.Append("\"comment\":\"Resetting test for next run.\""); jsonUpdates.Append("}"); if (i + 1 < list.Count) { jsonUpdates.Append(","); } } } jsonUpdates.Append("]"); jsonUpdates.Append("}"); string jsonFinal = jsonUpdates.ToString().Replace("},]", "}]"); client.SendPost(string.Format("add_results/{0}", args.First()), jsonFinal); return("Test statuses set to Untested. Please check the test run in your browser to confirm."); } else { return("The provided TestRails TestRunID must be a valid integer."); } }
//Report test results to TestRails. private static void SendResultForAllCasesToTestRails(string status, int testRunId, List <int> testCaseIds) { StringBuilder json = new StringBuilder(); json.Append("{\"results\": ["); int idStatus = TEST_RAILS_STATUSES[status]; for (int x = 0; x < testCaseIds.Count; x++) { string comment = string.Empty; string deviceDetails = AutomationMaster.GameMaster.GetDeviceDetails(); if (status == FAILED_NAME) { comment = string.Format("FROM TEST METHOD - {0} - FAILED! Details: {1} [{2}]", AutomationMaster.CurrentTestContext.TestName, AutomationMaster.CurrentTestContext.ErrorDetails, deviceDetails); } else { comment = string.Format("FROM TEST METHOD - {0} - {1} [{2}]", AutomationMaster.CurrentTestContext.TestName, status.ToUpper(), deviceDetails); } json.AppendLine("{"); json.Append(string.Format("\"test_id\":{0},", testCaseIds[x])); json.Append(string.Format("\"status_id\":{0},", idStatus)); json.Append(string.Format("\"comment\":\"{0}\"", comment)); json.Append("}"); if (x + 1 < testCaseIds.Count) { json.Append(","); } } json.Append("]"); json.Append("}"); TestRailsAPIClient client = new TestRailsAPIClient(GameMaster.BASE_TEST_RAILS_URL); AutoConsole.PostMessage(client.SendPost(string.Format("add_results/{0}", testRunId), json.ToString())); for (int t = 0; t < testCaseIds.Count; t++) { string jsonResult = client.GetTestName(testCaseIds[t]); //Only report as failure if assertion has not already failed, leading to these test cases being marked as fails. if (!Q.assert.IsFailing) { AutomationMaster.CurrentTestContext.AddAssertion(string.Format("<a href='{0}{1}'>{2}</a>", TestRailsAPIClient.GetUrl(), testCaseIds[t], jsonResult)); } else { AutomationMaster.CurrentTestContext.AddTestCaseAssertionOnFailure(string.Format("**TRY_FAIL**<a href='{0}{1}'>{2}</a>", TestRailsAPIClient.GetUrl(), testCaseIds[t], jsonResult)); } } }
public static void CopyFile(string copyThis, string destination) { if(!Exists(copyThis)) { AutoConsole.PostMessage(string.Format("Could not copy \"{0}\" to \"{1}\". File to copy does not currently exist.", copyThis.Replace("/", FILE_PATH_SPLIT), destination.Replace("/", FILE_PATH_SPLIT)), MessageLevel.Abridged); } if(Exists(destination)) { File.Delete(destination); } File.Copy(copyThis, destination); AutoConsole.PostMessage(string.Format("Copied \"{0}\" to \"{1}\"", copyThis, destination), MessageLevel.Verbose); }
public void UpdateKey(string key, string newValue) { if (_requiredConfigs.FindAll(x => x.Key == key).Any()) { _requiredConfigs.RemoveAt(_requiredConfigs.FindIndex(x => x.Key == key)); _requiredConfigs.Add(new KeyValuePair <string, string>(key, newValue)); } else if (_customConfigs.FindAll(x => x.Key == key).Any()) { _customConfigs.RemoveAt(_customConfigs.FindIndex(x => x.Key == key)); _customConfigs.Add(new KeyValuePair <string, string>(key, newValue)); } else { AutoConsole.PostMessage(string.Format("ConfigReader.UpdateKey() called for key \"{0}\" with value \"{1}\", but key does not exist in the \"{2}\" config!", key, newValue, System.Enum.GetName(typeof(FileResource), ResourceOrigin)), MessageLevel.Abridged); } }
public static void SaveNonUnityTextResource(FileResource file, string value, bool overwrite = true){ if(!isSet) { Set(); } #if UNITY_EDITOR if(!knownEditorResourceFiles.FindAll(x => x.Key == file).Any()) { AutoConsole.PostMessage("The supplied file could not be found. Make sure you are referencing a file stored outside of the Unity project."); //No usage of string.Format with file name data. Using Enum.GetName here causes exceptions on compile "Recursive Serialization is not supported. You can't dereference a PPtr while loading.". return; } string directory = string.Format("{0}{1}{2}", RESOURCES_DIRECTORY, FILE_PATH_SPLIT, knownEditorResourceFiles.Find(x => x.Key == file).Value); if(!Directory.Exists(Path.GetDirectoryName(directory))) { Set(); } if(!Exists(directory)) { Set(); } if(overwrite) { File.WriteAllText(directory, value); } else { File.AppendAllText(directory, value); } #endif }
public IEnumerator HandleCommand(string command) { if (!commands.Any()) { SetCommands(); } List <KeyValuePair <string, IEnumerator> > match = commands.FindAll(x => x.Key == command); if (match.Any()) { AutoConsole.PostMessage(string.Format("Running Buddy command \"{0}\"", command), MessageLevel.Abridged); yield return(StartCoroutine(match.First().Value)); } else { AutoConsole.PostMessage(string.Format("Cannot find Buddy command \"{0}\"", command), MessageLevel.Abridged); //TODO: Auto fail test if its pre-run commands cannot be accomodated. } yield return(null); }
public static void SendJsonInPieces(string jsonAttributePrefix, string json, int orderRan = -1) { //Break this message into pieces. List <string> pieces = new List <string>(); int charPos = 0; if (ConnectionStrategy.MaxMessageLength <= 0) { throw new UnityException("ConnectionStrategy.MaxMessageLength must be a positive number. This would cause an infinite loop and hard crash. Please fix the source of this value and rerun automation."); } while (charPos < json.Length) { if (charPos + ConnectionStrategy.MaxMessageLength <= json.Length) { pieces.Add(json.Substring(charPos, ConnectionStrategy.MaxMessageLength)); charPos += ConnectionStrategy.MaxMessageLength; } else { pieces.Add(json.Substring(charPos, json.Length - charPos - 1)); break; } } for (int x = 0; x < pieces.Count; x++) { if (orderRan >= 0) { AutoConsole.PostMessage(string.Format("{5}_MULTI_PART|{1}{0}{2}{0}{3}{0}{4}|", AutomationMaster.PARTIAL_DELIMITER, x, pieces.Count, orderIndex, pieces[x], jsonAttributePrefix), MessageLevel.Abridged); } else { AutoConsole.PostMessage(string.Format("{0}_MULTI_PART|{1}{2}{3}|", jsonAttributePrefix, x, AutomationMaster.PARTIAL_DELIMITER, pieces[x]), MessageLevel.Abridged); } } }
IEnumerator SendCommand(ServerCommand command) { if (AutomationMaster.Arbiter.TestRunId.Length == 0 || Application.isEditor) { AutoConsole.PostMessage(string.Format("Ignoring command \"{0}\" as no server is involved in this test run.", Enum.GetName(typeof(ServerCommand), command)), MessageLevel.Verbose); yield break; } switch (command) { case ServerCommand.AcceptDeviceAlert: //AutomationMaster.Arbiter.SendCommunication("SERVER_BROKER_COMMAND|HANDLE_DEVICE_ALERT|", "SERVER_BROKER_VALUE|1|"); // 1 or true, accept alert. yield break; case ServerCommand.DeclineDeviceAlert: //AutomationMaster.Arbiter.SendCommunication("SERVER_BROKER_COMMAND|HANDLE_DEVICE_ALERT|", "SERVER_BROKER_VALUE|0|"); // 0 or false, decline alert. yield break; } /* * float timer = 0f; * _awaitingResponse = true; * while(_awaitingResponse && timer <= _responseTimeout) { * * timer += 1; * yield return StartCoroutine(Q.driver.WaitRealTime(1f)); * * } * if(timer > _responseTimeout) { * * yield return StartCoroutine(Q.assert.Fail("Response timeout occurred waiting for server to complete and respond to a ServerBroker command.")); * * } */ yield return(null); }
//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(); } } }
static void Set() { isSet = true; string basePath = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase); if(Application.platform == RuntimePlatform.WindowsEditor) { if(basePath.Contains(@"\\")) { FILE_PATH_SPLIT = @"\\"; } else { FILE_PATH_SPLIT = @"\"; } } else { FILE_PATH_SPLIT = "/"; } BASE_RESOURCE_PATH = string.Format("{0}{1}Automation{1}Engine{1}Xtra{1}Resources{1}", Application.dataPath.Replace("/", FILE_PATH_SPLIT), FILE_PATH_SPLIT); knownTrilleonResourceFiles.Add(new KeyValuePair<FileResource,string>(FileResource.TrilleonConfig, "TrilleonConfig")); knownTrilleonResourceFiles.Add(new KeyValuePair<FileResource,string>(FileResource.ReportJavascript, ConfigReader.GetString("AUTOMATION_RESULTS_REPORT_JAVASCRIPT_USE").Replace("/", FILE_PATH_SPLIT))); knownTrilleonResourceFiles.Add(new KeyValuePair<FileResource,string>(FileResource.ReportCss, ConfigReader.GetString("AUTOMATION_RESULTS_REPORT_CSS_USE").Replace("/", FILE_PATH_SPLIT))); #if UNITY_EDITOR AssetDatabase.Refresh(); knownEditorResourceFiles.Add(new KeyValuePair<FileResource,string>(FileResource.LatestTestResults, "nexus_test_manifest_latest_test_results.txt")); knownEditorResourceFiles.Add(new KeyValuePair<FileResource,string>(FileResource.LaunchInstructions, "nexus_test_manifest_launch_instructions.txt")); knownEditorResourceFiles.Add(new KeyValuePair<FileResource,string>(FileResource.ManifestGUISettings, "nexus_test_manifest_categories_foldout_bools.txt")); knownEditorResourceFiles.Add(new KeyValuePair<FileResource,string>(FileResource.BuddyHistory, "nexus_buddy_data_history.txt")); knownEditorResourceFiles.Add(new KeyValuePair<FileResource,string>(FileResource.NexusTabs, "nexus_tab_preferences.txt")); knownEditorResourceFiles.Add(new KeyValuePair<FileResource,string>(FileResource.Favorites, "nexus_manifest_favorites.txt")); knownEditorResourceFiles.Add(new KeyValuePair<FileResource,string>(FileResource.FavoritesUnit, "nexus_manifest_favorites_unit.txt")); BASE_NON_UNITY_PATH = string.Empty; if(Application.platform == RuntimePlatform.WindowsEditor) { BASE_NON_UNITY_PATH = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); } else { string[] pathPieces = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase).Split(new string[] { FILE_PATH_SPLIT }, StringSplitOptions.None); if(pathPieces.Length <= 2) { string error = string.Format("Unrecognized file path encountered. Trilleon cannot interact with local files, and will not be fully functional until the issue is corrected. Path [{0}]", basePath); AutoConsole.PostMessage(error); Debug.Log(error); return; } BASE_NON_UNITY_PATH = string.Format("/{0}/{1}", pathPieces[1], pathPieces[2]); } RESOURCES_DIRECTORY = string.Format("{0}{4}{1}{4}{2}{3}", BASE_NON_UNITY_PATH, TOP_LEVEL_FOLDER_NAME, GameMaster.GAME_NAME, ConfigReader.GetString("EDITOR_RESOURCE_FILES_DIRECTORY").Replace("/", FILE_PATH_SPLIT), FILE_PATH_SPLIT); if(!Directory.Exists(RESOURCES_DIRECTORY)) { Directory.CreateDirectory(Path.GetDirectoryName(RESOURCES_DIRECTORY)); } CONSOLE_LOG_DIRECTORY = string.Format("{0}{4}{1}{4}{2}{3}", BASE_NON_UNITY_PATH, TOP_LEVEL_FOLDER_NAME, GameMaster.GAME_NAME, ConfigReader.GetString("EDITOR_RESOURCE_CONSOLE_LOG_DIRECTORY").Replace("/", FILE_PATH_SPLIT), FILE_PATH_SPLIT); if(!Directory.Exists(CONSOLE_LOG_DIRECTORY)) { Directory.CreateDirectory(Path.GetDirectoryName(CONSOLE_LOG_DIRECTORY)); } REPORTS_DIRECTORY = string.Format("{0}{4}{1}{4}{2}{3}", BASE_NON_UNITY_PATH, TOP_LEVEL_FOLDER_NAME, GameMaster.GAME_NAME, ConfigReader.GetString("EDITOR_RESOURCE_TEST_REPORTS_DIRECTORY").Replace("/", FILE_PATH_SPLIT), FILE_PATH_SPLIT); if(!Directory.Exists(REPORTS_DIRECTORY)) { Directory.CreateDirectory(Path.GetDirectoryName(REPORTS_DIRECTORY)); } SCREENSHOTS_DIRECTORY = string.Format("{0}screenshots{1}", RESOURCES_DIRECTORY, FILE_PATH_SPLIT); if(!Directory.Exists(SCREENSHOTS_DIRECTORY)) { //This is the single report screenshot storage directory. Directory.CreateDirectory(Path.GetDirectoryName(SCREENSHOTS_DIRECTORY)); } string file = string.Format("{0}TrilleonConfig.txt", BASE_RESOURCE_PATH); FileInfo fileInfo = new FileInfo(file); fileInfo.IsReadOnly = false; file = string.Format("{0}{1}.txt", BASE_RESOURCE_PATH, ConfigReader.GetString("AUTOMATION_RESULTS_REPORT_JAVASCRIPT_USE").Replace("/", FILE_PATH_SPLIT), FILE_PATH_SPLIT); fileInfo = new FileInfo(file); fileInfo.IsReadOnly = false; file = string.Format("{0}{1}.txt", BASE_RESOURCE_PATH, ConfigReader.GetString("AUTOMATION_RESULTS_REPORT_CSS_USE").Replace("/", FILE_PATH_SPLIT)); fileInfo = new FileInfo(file); fileInfo.IsReadOnly = false; //Create any missing required files. for(int k = 0; k < knownEditorResourceFiles.Count; k++) { string fileName = string.Format("{0}{1}{2}", RESOURCES_DIRECTORY, FILE_PATH_SPLIT, knownEditorResourceFiles[k].Value); if(!Exists(fileName)) { File.WriteAllText(fileName, string.Empty); } } #endif }
/// <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); } } } }
private string SendRequest(string method, string uri, string json) { try { string url = this.m_url + uri; // Create the request object and set the required HTTP method // (GET/POST) and headers (content type and basic auth). HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); request.ContentType = "application/json"; request.Method = method; string auth = Convert.ToBase64String( Encoding.ASCII.GetBytes( String.Format( "{0}:{1}", this.m_user, this.m_password ) ) ); request.Headers.Add("Authorization", "Basic " + auth); if (method == "POST") { // Add the POST arguments, if any. We just serialize the passed // data object (i.e. a dictionary) and then add it to the request // body. using (var streamWriter = new StreamWriter(request.GetRequestStream())) { streamWriter.Write(json); streamWriter.Flush(); streamWriter.Close(); } /*if (!string.IsNullOrEmpty(json)) * { * byte[] block = Encoding.UTF8.GetBytes( * json * ); * request.GetRequestStream().Write(block, 0, block.Length); * }*/ } // Execute the actual web request (GET or POST) and record any // occurred errors. Exception ex = null; HttpWebResponse response = null; try { response = (HttpWebResponse)request.GetResponse(); } catch (WebException e) { if (e.Response != null) { response = (HttpWebResponse)e.Response; ex = e; } } // Read the response body, if any, and deserialize it from JSON. string text = ""; if (response != null) { var reader = new StreamReader( response.GetResponseStream(), Encoding.UTF8 ); using (reader){ text = reader.ReadToEnd(); } } // Check for any occurred errors and add additional details to // the exception message, if any (e.g. the error message returned // by TestRail). if (ex != null) { string error = text; if (error != null) { error = '"' + error + '"'; } else { error = "No additional error message received"; } AutoConsole.PostMessage(String.Format("TestRail API returned HTTP {0} ({1}) for IdTest {2} with JSON [{3}]", (int)response.StatusCode, error, uri, json), MessageLevel.Abridged); } return(text); } catch (Exception e) { return(e.Message); } }
/// <summary> /// Create x /// </summary> public static void AddToReport(bool isSuccess, double runTime, bool skipped = false) { if (AutomationMaster.ValidationRun) { return; } string testName = AutomationMaster.CurrentTestContext.TestName; ConsoleMessageType type; string status = string.Empty; List <string> assertions = AutomationMaster.CurrentTestContext.Assertions; if (skipped && isSuccess) { testName = AutomationMaster.TestRunContext.Ignored.Tests[AutomationMaster.TestRunContext.Ignored.Tests.Count - 1]; xmlBody.Append(string.Format("<testcase classname=\"{0}\" name=\"{1}\" time=\"{2}\" status=\"ignored\"></testcase>", AutomationMaster.CurrentTestContext.ClassName, testName, 0)); status = IGNORED_NAME; type = ConsoleMessageType.AssertionIgnore; } else if (skipped && !isSuccess) { xmlBody.Append(string.Format("<testcase classname=\"{0}\" name=\"{1}\" time=\"{2}\" status=\"skipped\">", AutomationMaster.CurrentTestContext.ClassName, testName, 0)); xmlBody.Append(string.Format("<skipped message=\"{0}\" type=\"Test Failure\"></skipped></testcase>", assertions.Any() ? assertions.Last() : "Error occurred before any test assertions were made.")); status = SKIPPED_NAME; type = ConsoleMessageType.AssertionSkip; SkipFailCount++; } else if (AutomationMaster.CurrentTestContext.IsSuccess) { xmlBody.Append(string.Format("<testcase classname=\"{0}\" name=\"{1}\" time=\"{2}\"></testcase>", AutomationMaster.CurrentTestContext.ClassName, testName, runTime)); status = PASSED_NAME; type = ConsoleMessageType.AssertionPass; } else { xmlBody.Append(string.Format("<testcase classname=\"{0}\" name=\"{1}\" time=\"{2}\">", AutomationMaster.CurrentTestContext.ClassName, testName, runTime)); xmlBody.Append(string.Format("<failure message=\"{0}\" type=\"Test Failure\"></failure></testcase>", assertions.Any() ? assertions.Last() : "Error occurred before any test assertions were made.")); status = FAILED_NAME; type = ConsoleMessageType.AssertionFail; } TestSuiteRunTime += runTime; AutoConsole.PostMessage(string.Format("{0} {1}", status, AutomationMaster.CurrentTestContext.TestName), MessageLevel.Abridged, type, AutomationMaster.CurrentTestContext.TestName); #if UNITY_EDITOR SaveMostRecentsResults(status); #endif StringBuilder assertionsJson = new StringBuilder(); for (int a = 0; a < AutomationMaster.CurrentTestContext.Assertions.Count; a++) { //Note; Replace "[{" to prevent the reading of any encoded json as actual JSON, which may cause a parsing failure in the html report. assertionsJson.Append(string.Format("{{\"assertion\":\"{0}\"}}{1}", AutomationMaster.CurrentTestContext.Assertions[a].Replace("[{", "||"), (AutomationMaster.CurrentTestContext.Assertions.Count - 1) == a ? string.Empty : ",")); } orderIndex++; string error_details = AutomationMaster.CurrentTestContext.Notices.Any() ? string.Join(", ", AutomationMaster.CurrentTestContext.Notices.ToArray()) : string.Empty; error_details += AutomationMaster.CurrentTestContext.ErrorDetails.Replace("[{", "||"); error_details = error_details ?? "No Error Details Were Reported For This Failure!"; string json = string.Format("{{\"order_ran\":\"{0}\", \"status\":\"{1}\", \"name\":\"{2}\", \"class\":\"{3}\", \"test_categories\":\"{4}\", \"result_details\":\"{5}\", \"assertions\":[{6}]}},", orderIndex, status, AutomationMaster.CurrentTestContext.TestName, AutomationMaster.CurrentTestContext.ClassName, string.Join(", ", AutomationMaster.CurrentTestContext.Categories.ToArray()), error_details, assertionsJson.ToString()); if (json.Length > ConnectionStrategy.MaxMessageLength) { SendJsonInPieces("SINGLE_TEST_RESULTS_JSON", json, orderIndex); jsonBody.Append(json); } else { jsonBody.Append(json); AutoConsole.PostMessage(string.Format("SINGLE_TEST_RESULTS_JSON|{0}|", json), MessageLevel.Abridged); } }
private void Error(PubnubClientError pubnubError) { AutoConsole.PostMessage(pubnubError.Description, MessageLevel.Verbose); }
public override void Render() { GUIStyle horizontal = new GUIStyle(); horizontal.margin = new RectOffset(10, 11, 0, 0); GUIStyle statusColor = new GUIStyle(GUI.skin.label); statusColor.margin = new RectOffset(0, 0, 3, 0); statusColor.normal.textColor = AutoConsole.Paused ? Color.red : Nexus.TextGreen; statusColor.fixedWidth = 125; statusColor.fontStyle = FontStyle.Bold; GUILayout.Space(15); EditorGUILayout.BeginHorizontal(horizontal); //For "Running" notification and FPS counter. if (Application.isPlaying && AutomationMaster.Busy) { EditorGUILayout.LabelField("Running Test(s)", statusColor); } else { AutomationMaster.Busy = false; EditorGUILayout.LabelField(string.Empty, statusColor); } if (Application.isPlaying) { if (renderPasses == FPS_SAMPLE_RATE && Application.isPlaying) { fpsSample = Math.Round(1 / Time.deltaTime, 0); renderPasses = 0; } GUIStyle fps = new GUIStyle(GUI.skin.label); fps.fontSize = 26; fps.padding = new RectOffset(0, 0, -8, 0); fps.normal.textColor = GetFpsColor(fpsSample); GUILayout.FlexibleSpace(); EditorGUILayout.LabelField(string.Format("FPS {0}", fpsSample), new GUILayoutOption[] { GUILayout.Width(50) }); EditorGUILayout.LabelField(string.Format("◈", fpsSample), fps, new GUILayoutOption[] { GUILayout.Width(35) }); GUILayout.Space(-18); renderPasses++; } EditorGUILayout.EndHorizontal(); //End for "Running" notification and FPS counter. GUIStyle scrollStatus = new GUIStyle(GUI.skin.button); scrollStatus.margin = new RectOffset(0, 0, 0, 0); scrollStatus.fontStyle = _autoScroll ? FontStyle.Bold : FontStyle.Normal; scrollStatus.normal.background = Swat.ToggleButtonBackgroundSelectedTexture; scrollStatus.normal.textColor = _pubsubMode ? Nexus.TextGreen : Swat.WindowDefaultTextColor; EditorGUILayout.Space(); GUILayout.BeginHorizontal(horizontal); EditorGUILayout.LabelField(string.Format("{0} - {1}", AutoConsole.Paused ? "Paused" : "Active", AutoConsole.FilterLevel == MessageLevel.Verbose ? "Verbose" : "Abridged"), statusColor); EditorGUILayout.Space(); GUILayout.Space(-100); Nexus.Self.Button("Pubsub", "Show received Pubsub messages.", new Nexus.SwatDelegate(delegate() { _pubsubMode = !_pubsubMode; }), scrollStatus, new GUILayoutOption[] { GUILayout.Width(75), GUILayout.Height(22) }); GUILayout.Space(0.65f); scrollStatus.normal.textColor = _autoScroll ? Nexus.TextGreen : Color.red; Nexus.Self.Button("Auto Scroll", "Remove all messages in the console.", new Nexus.SwatDelegate(delegate() { _autoScroll = !_autoScroll; }), scrollStatus, new GUILayoutOption[] { GUILayout.Width(75), GUILayout.Height(22) }); GUILayout.EndHorizontal(); EditorGUILayout.Space(); GUILayout.BeginHorizontal(horizontal); GUIStyle button = new GUIStyle(GUI.skin.button); button.margin = new RectOffset(0, 0, 0, 0); button.normal.textColor = Swat.ToggleButtonTextColor; button.normal.background = Swat.ToggleButtonBackgroundTexture; Nexus.Self.Button("Clear", "Remove all messages in the console.", new Nexus.SwatDelegate(delegate() { FileBroker.SaveUnboundFile(string.Format("{0}{1}{2}/console_before_start.txt", FileBroker.BASE_NON_UNITY_PATH, ConfigReader.GetString("EDITOR_RESOURCE_CONSOLE_LOG_DIRECTORY"), GameMaster.GAME_NAME), string.Empty); AutoConsole.messageListDisplayed = new List <AutoConsoleMessage>(); AutoConsole.messageListQueued = new List <AutoConsoleMessage>(); _selectedConsoleMessage = null; }), button, new GUILayoutOption[] { GUILayout.Width(60), GUILayout.Height(22) }); GUILayout.Space(0.5f); Nexus.Self.Button(AutoConsole.Paused ? "Resume" : "Pause", "Pause/Continue rendering of new messages in the console. Messages are still received and stored when paused, and will appear immediately when resuming.", new Nexus.SwatDelegate(delegate() { AutoConsole.Paused = !AutoConsole.Paused; }), button, new GUILayoutOption[] { GUILayout.Width(60), GUILayout.Height(22) }); EditorGUILayout.Space(); Nexus.Self.Button("Verbose", "Post ALL messages from the automation framework in the console.", new Nexus.SwatDelegate(delegate() { AutoConsole.FilterLevel = MessageLevel.Verbose; if (_autoScroll) { _scroll.y = 99999; _scroll.x = 0; } }), button, new GUILayoutOption[] { GUILayout.Width(75), GUILayout.Height(22) }); GUILayout.Space(0.65f); GUIStyle abridged = new GUIStyle(GUI.skin.button); abridged.margin = new RectOffset(0, 0, 0, 0); abridged.normal.textColor = Swat.ToggleButtonTextColor; abridged.normal.background = Swat.ToggleButtonBackgroundTexture; Nexus.Self.Button("Abridged", "Post only automation messages marked as high priority in the console.", new Nexus.SwatDelegate(delegate() { AutoConsole.FilterLevel = MessageLevel.Abridged; if (_autoScroll) { _scroll.y = 99999; _scroll.x = 0; } }), abridged, new GUILayoutOption[] { GUILayout.Width(75), GUILayout.Height(22) }); GUILayout.EndHorizontal(); float messageHeight = 15; GUIStyle consoleBox = new GUIStyle(GUI.skin.box); consoleBox.fixedHeight = Nexus.Self.position.height - (Application.isPlaying ? 125 : 105); consoleBox.fixedWidth = Nexus.Self.position.width - 20; consoleBox.margin = new RectOffset(10, 0, 0, 10); consoleBox.normal.background = Swat.TabButtonBackgroundTexture; GUILayout.BeginVertical(consoleBox); //Begin box border around console. _scroll = GUILayout.BeginScrollView(_scroll); GUIStyle messageBox = new GUIStyle(GUI.skin.box); messageBox.normal.background = Swat.TabButtonBackgroundSelectedTexture; messageBox.margin = new RectOffset(2, 2, 1, 1); GUIStyle messageBoxText = new GUIStyle(GUI.skin.label); messageBoxText.normal.textColor = Swat.WindowDefaultTextColor; messageBox.fixedWidth = consoleBox.fixedWidth - 10; messageBoxText.fixedWidth = messageBox.fixedWidth - 5; string lastConsoleMessage = string.Empty; int duplicateCount = 0; List <AutoConsoleMessage> consoleMessages = AutoConsole.messageListDisplayed; for (int m = 0; m < consoleMessages.Count; m++) { if ((_pubsubMode && consoleMessages[m].messageType != ConsoleMessageType.Pubsub) || (!_pubsubMode && consoleMessages[m].messageType == ConsoleMessageType.Pubsub)) { continue; } //If this is a new console message, and auto scroll is on, scroll down automatically. if (_autoScroll && m == _lastPassConsoleMessageCount) { _scroll.y = 99999; _scroll.x = 0; } //Render only messages of the requested filter level. if (_pubsubMode || (AutoConsole.FilterLevel == MessageLevel.Abridged && consoleMessages[m].level == MessageLevel.Abridged) || AutoConsole.FilterLevel == MessageLevel.Verbose) { messageBoxText.normal.textColor = AutoConsole.MessageColor(consoleMessages[m].messageType); GUILayout.BeginHorizontal(messageBox, new GUILayoutOption[] { GUILayout.MinWidth(225), GUILayout.MaxHeight(messageHeight) }); if (lastConsoleMessage == consoleMessages[m].message) { duplicateCount++; consoleMessages.RemoveAt(m - 1); } else { lastConsoleMessage = consoleMessages[m].message; duplicateCount = 0; } if (consoleMessages.Count > m) { if (GUILayout.Button(string.Format("{0} {1}", duplicateCount > 0 ?(duplicateCount + 1).ToString() : string.Empty, consoleMessages[m].message), messageBoxText)) { if (_selectedConsoleMessage == consoleMessages[m]) { //If the message has been double-clicked, open this test failure as a single-test, temporary report. if (DateTime.Now.Subtract(_lastConsoleMessageButtonClick).TotalSeconds < 0.75d && !string.IsNullOrEmpty(consoleMessages[m].testMethod)) { if (_consoleAbridgedReportTypes.Contains(consoleMessages[m].messageType)) { _selectedConsoleMessage = null; //Prevents details panel from appearing. We only want it to appear for a single click, not a double click. AutomationReport.BuildTemporaryReportForSingleConsoleFailure(consoleMessages[m].testMethod); System.Diagnostics.Process.Start(string.Format("{0}SingleTestAsReportTemp.html", FileBroker.RESOURCES_DIRECTORY)); OpenedTestReport = DateTime.UtcNow; } } else { ConsoleMessage.Pop(_selectedConsoleMessage.message, Enum.GetName(typeof(ConsoleMessageType), _selectedConsoleMessage.messageType), Enum.GetName(typeof(MessageLevel), _selectedConsoleMessage.level), _selectedConsoleMessage.timestamp.ToString()); _selectedConsoleMessage = null; } } _selectedConsoleMessage = consoleMessages[m]; _lastConsoleMessageButtonClick = DateTime.Now; } } GUILayout.EndHorizontal(); } } _lastPassConsoleMessageCount = consoleMessages.Count; GUILayout.EndScrollView(); GUILayout.EndVertical(); }
public IEnumerator RunBuddySystemTests() { //If a relationship has not yet been established, then cancel BuddySystem execution. This is not used if IgnoreAllBuddyTests flag set. BuddySystemHandlingStarted = true; _isBuddySystemFailure = string.IsNullOrEmpty(BuddyName); if (!AutomationMaster.IgnoreAllBuddyTests) { //If allowed, and Buddy is not yet set, then set Buddy to last-used Buddy. #if UNITY_EDITOR if (AutomationMaster.ConfigReader.GetBool("EDITOR_DEFAULT_BUDDY_TO_LAST")) { string mostRecentBuddy = AutomationReport.GetMostRecentBuddy(); if (!string.IsNullOrEmpty(mostRecentBuddy)) { BuddyName = mostRecentBuddy; } } #endif if (_isBuddySystemFailure) { _failureReason = "Buddy was not set before BuddySystem test execution started."; } if (!_isBuddySystemFailure) { AutoConsole.PostMessage("Establishing Connection With Buddy", MessageLevel.Abridged); do { if (RoleGridLock) { _isBuddySystemFailure = true; _failureReason = "This client and the associated Buddy client share the same role (primary/secondary). One must be a primary Buddy, and the other a secondary Buddy."; break; } SendBuddyCommunication("buddy_ready_for_tests", IsPrimary ? "primary" : "secondary"); yield return(StartCoroutine(Q.driver.WaitRealTime(5))); time += 5; } while((!_isBuddyReadyForBuddyTests || !HasBuddyAcknowledgedOurReadiness) && time <= timeout); AutoConsole.PostMessage(time > timeout ? "Buddy Connection Failure": "Buddy Connection Established", MessageLevel.Abridged); if (IsPrimary && !BuddyTestRequiredDetails.Any()) { //If this client is the Primary, and has not yet recieved its required information from the Secondary, request it. for (int limit = 30; limit >= 0; limit--) { if (!BuddyTestRequiredDetails.Any()) { break; } SendBuddyCommunication("buddy_requesting_required_details", "0"); yield return(StartCoroutine(Q.driver.WaitRealTime(1))); } } if (time >= timeout || (IsPrimary && !BuddyTestRequiredDetails.Any())) { _isBuddySystemFailure = true; _failureReason = "Timed out waiting for Buddy to be ready for multi-client testing."; } if (!_isBuddySystemFailure) { SendBasicBuddyDetails(); if (_isPrimary) { yield return(StartCoroutine(PrimaryBuddyTestRun())); if (!_isBuddySystemFailure) { ResetBuddySystemValues(); yield return(StartCoroutine(Q.driver.WaitRealTime(5))); SendBuddyCommunication("buddy_switching_roles", "0"); timeout = 300; time = 0; while (!BuddyHasSuccessfullySwitchRoles && time <= timeout) { yield return(StartCoroutine(Q.driver.WaitRealTime(5))); SendBuddyCommunication("buddy_primary_complete_action_tests", "0"); time += 5; } if (time > timeout) { _isBuddySystemFailure = true; _failureReason = "Timed out waiting for Buddy to switch roles from Secondary to Primary."; } else { AutoConsole.PostMessage("Switching Roles With Buddy", MessageLevel.Abridged); yield return(StartCoroutine(SecondaryBuddyTestRun())); } } } else { yield return(StartCoroutine(SecondaryBuddyTestRun())); if (!_isBuddySystemFailure) { ResetBuddySystemValues(); yield return(StartCoroutine(Q.driver.WaitRealTime(5))); SendBuddyCommunication("buddy_switching_roles", "0"); timeout = 300; time = 0; while (!BuddyHasSuccessfullySwitchRoles && time <= timeout) { yield return(StartCoroutine(Q.driver.WaitRealTime(5))); SendBuddyCommunication("buddy_secondary_tests_complete", "0"); time += 5; } if (time > timeout) { _isBuddySystemFailure = true; _failureReason = "Timed out waiting for Buddy to switch roles from Primary to Secondary."; } else { AutoConsole.PostMessage("Switching Roles With Buddy", MessageLevel.Abridged); yield return(StartCoroutine(PrimaryBuddyTestRun())); } } } SendBuddyCommunication("buddy_tearing_down", "0"); yield return(StartCoroutine(Q.driver.WaitRealTime(5))); SendBuddyCommunication("buddy_tearing_down", "0"); } } } if (_isBuddySystemFailure || AutomationMaster.IgnoreAllBuddyTests) { //Fail all remaining tests. string errorMessage = string.Format("BuddySystem failure. Reason: {0} Skipping BuddySystem tests.", _failureReason); AutoConsole.PostMessage(errorMessage, MessageLevel.Abridged); for (int f = 0; f < _buddies.Count; f++) { if (!HandledBuddyTests.Contains(_buddies[f].Key.Key)) { AutomationMaster.CurrentTestContext = new TestContext(); if (!AutomationMaster.Methods.KeyValListContainsKey(_buddies[f].Key.Key)) { AutomationMaster.Methods.Add(new KeyValuePair <string, MethodInfo>(_buddies[f].Key.Key, Buddies[f].Key.Value)); } } yield return(StartCoroutine(Master.LaunchSingleTest(_buddies[f].Key, AutomationMaster.Methods.Count - 1, AutomationMaster.IgnoreAllBuddyTests ? TestStatus.Ignore : TestStatus.Fail, errorMessage))); for (int fr = 0; fr < _buddies[f].Value.Count; fr++) { if (HandledBuddyTests.Contains(Buddies[f].Value[fr].Value.Name)) { continue; } AutomationMaster.CurrentTestContext = new TestContext(); if (!AutomationMaster.Methods.KeyValListContainsKey(_buddies[f].Value[fr].Key)) { AutomationMaster.Methods.Add(new KeyValuePair <string, MethodInfo>(_buddies[f].Value[fr].Key, Buddies[f].Value[fr].Value)); } yield return(StartCoroutine(Master.LaunchSingleTest(_buddies[f].Value[fr], AutomationMaster.Methods.Count - 1, AutomationMaster.IgnoreAllBuddyTests ? TestStatus.Ignore : TestStatus.Fail, errorMessage))); } } } HandledBuddyTests = new List <string>(); ResetBuddySystemValues(); yield return(null); }
/// <summary> /// While this client is in the Primary Buddy role, run Action tests, and await completion of Secondary Buddy's Reaction tests. /// </summary> /// <returns>The buddy test run.</returns> public IEnumerator PrimaryBuddyTestRun() { for (int b = 0; b < _buddies.Count; b++) { //Skip this method if it has already been handled. if (HandledBuddyTests.Contains(Buddies[b].Key.Key)) { continue; } SecondaryReactionsStarted = false; //Reset for next reaction. timeout = 180; time = 0; //Retrieve any BuddyCommands for this Action test, and send them to Buddy for execution. BuddyCommand command = _buddies[b].Key.Value.GetCustomAttributes(typeof(BuddyCommand), false).ToList().First() as BuddyCommand; if (command != null) { SendBuddyCommunication("buddy_primary_pretest_commands", string.Join("|", command.Commands.ToArray())); BuddyProcessingCommands = true; while (BuddyProcessingCommands && time <= timeout) { if (BuddyCommandExecutionFailure) { break; } //Every 50 seconds, report still waiting. if (time % 50 == 0) { AutoConsole.PostMessage(string.Format("Primary waiting for Secondary to complete pretest commands ({0}) seconds", time.ToString()), MessageLevel.Abridged); } yield return(StartCoroutine(Q.driver.WaitRealTime(5))); time += 5; } } string errorMessage = string.Empty; HandledBuddyTests.Add(_buddies[b].Key.Key); if (!AutomationMaster.Methods.KeyValListContainsKey(Buddies[b].Key.Key)) { AutomationMaster.Methods.Add(new KeyValuePair <string, MethodInfo>(Buddies[b].Key.Key, Buddies[b].Key.Value)); } if (!BuddyCommandExecutionFailure && time <= timeout) { //Launch Action test. yield return(StartCoroutine(Master.LaunchSingleTest(Buddies[b].Key, AutomationMaster.Methods.Count - 1))); } else { AutomationMaster.CurrentTestContext.TestInitialize(_buddies[b].Key.Value); if (BuddyCommandExecutionFailure) { errorMessage = string.Format("The Secondary Buddy of this client failed to successfully handle the command(s) {0} required by this Action test. Commands: {1}.", BuddyCommandFailure, string.Join("|", command.Commands.ToArray())); } else { errorMessage = "Timout occurred waiting for primary buddy to complete its pretest commands."; } yield return(StartCoroutine(Q.assert.Fail(errorMessage, FailureContext.TestMethod))); AutomationReport.AddToReport(false, 0, true); //Save results to test run's XML file. Master.ReportOnTest(); } ResendAllInfoForBuddyTests(); yield return(StartCoroutine(Q.driver.WaitRealTime(2f))); bool actionSuccessful = AutomationMaster.CurrentTestContext.IsSuccess; if (actionSuccessful) { SendBuddyCommunication("buddy_primary_test_complete", _buddies[b].Key.Value.Name); } else { SendBuddyCommunication("buddy_primary_test_failed", _buddies[b].Key.Value.Name); } //When the buddy has completed all reaction tests, we can then move on to the next. timeout = 300; time = 0; _waitingForBuddyToCompleteReactionTests = true; int resendCount = 4; while (_waitingForBuddyToCompleteReactionTests && time <= timeout) { //Check if buddy has declared that it is running its secondary tests. If not, resend the command once. if (resendCount > 0 && time > 10 && !SecondaryReactionsStarted) { resendCount--; if (actionSuccessful) { SendBuddyCommunication("buddy_primary_test_complete", _buddies[b].Key.Value.Name); } else { SendBuddyCommunication("buddy_primary_test_failed", _buddies[b].Key.Value.Name); } } //Every 50 seconds, report still waiting. if (time % 50 == 0) { AutoConsole.PostMessage(string.Format("Primary waiting for Secondary ({0}) seconds", time.ToString()), MessageLevel.Abridged); } yield return(StartCoroutine(Q.driver.WaitRealTime(5f))); time += 5; } //If we time out, stop the run and break out of execution. _isBuddySystemFailure = time >= timeout; if (_isBuddySystemFailure) { _failureReason = errorMessage; AutoConsole.PostMessage(_failureReason, MessageLevel.Abridged); break; } //Run all CounterReaction tests for this Action. List <KeyValuePair <string, MethodInfo> > counterReactions = _buddies.FindAll(x => x.Key.Value.Name == _buddies[b].Key.Value.Name).First().Value.FindAll(x => { BuddySystem bs = (BuddySystem)Attribute.GetCustomAttribute(x.Value, typeof(BuddySystem)); return(bs.buddy == Buddy.CounterReaction); }); for (int c = 0; c < counterReactions.Count; c++) { HandledBuddyTests.Add(counterReactions[c].Key); if (!AutomationMaster.Methods.KeyValListContainsKey(counterReactions[c].Key)) { AutomationMaster.Methods.Add(new KeyValuePair <string, MethodInfo>(counterReactions[c].Key, counterReactions[c].Value)); } yield return(StartCoroutine(Master.LaunchSingleTest(counterReactions[c], AutomationMaster.Methods.Count - 1, actionSuccessful ? TestStatus.Pass : TestStatus.Fail))); } } SendBuddyCommunication("buddy_primary_complete_action_tests", "0"); yield return(null); }
public IEnumerator SecondaryBuddyTestRun() { do { int timeout = 300; int time = 0; while (!ReadyForReactionTests && time <= timeout) { if (ReadyForReactionTests || IsPrimaryFinishedWithActionTests || PrimaryFailed || BuddyTearingDown) { break; } //Every 50 seconds, report still waiting. if (time % 50 == 0) { AutoConsole.PostMessage(string.Format("Secondary waiting for Primary ({0}) seconds", time.ToString()), MessageLevel.Abridged); } yield return(StartCoroutine(Q.driver.WaitRealTime(5f))); time += 5; } //If there is no primary test, then we are complete the secondary run. if (string.IsNullOrEmpty(CurrentPrimaryTest) || BuddyTearingDown) { break; } SendBuddyCommunication("buddy_starting_reaction", "0"); List <KeyValuePair <string, MethodInfo> > reactionTests = _buddies.FindAll(x => x.Key.Value.Name == CurrentPrimaryTest).First().Value.FindAll(x => { BuddySystem bs = (BuddySystem)Attribute.GetCustomAttribute(x.Value, typeof(BuddySystem)); return(bs.buddy == Buddy.Reaction); }); for (int r = 0; r < reactionTests.Count; r++) { if (_primaryFailed) { //Add each test and "start" them with auto fail. if (!AutomationMaster.Methods.KeyValListContainsKey(reactionTests[r].Key)) { AutomationMaster.Methods.Add(new KeyValuePair <string, MethodInfo>(reactionTests[r].Key, reactionTests[r].Value)); } string errorMessage = string.Format("This BuddySystem Reaction test was skipped because its primary Action \"{0}\" failed.", CurrentPrimaryTest); yield return(StartCoroutine(Master.LaunchSingleTest(reactionTests[r], AutomationMaster.Methods.Count - 1, TestStatus.Fail, errorMessage))); } else { if (HandledBuddyTests.Contains(reactionTests[r].Key)) { continue; } HandledBuddyTests.Add(reactionTests[r].Key); if (!AutomationMaster.Methods.KeyValListContainsKey(reactionTests[r].Key)) { AutomationMaster.Methods.Add(new KeyValuePair <string, MethodInfo>(reactionTests[r].Key, reactionTests[r].Value)); } yield return(StartCoroutine(Master.LaunchSingleTest(reactionTests[r], AutomationMaster.Methods.Count - 1))); SendBuddyCommunication("buddy_secondary_test_complete", reactionTests[r].Key); } } ReadyForReactionTests = false; CurrentPrimaryTest = string.Empty; //Alert buddy that reaction tests are complete. SendBuddyCommunication("buddy_secondary_tests_complete", "0"); ResendAllInfoForBuddyTests(); } while(!_isPrimaryFinishedWithActionTests); yield return(null); }
public IEnumerator Warn(string message, FailureContext failureContext = FailureContext.Default) { AutoConsole.PostMessage(string.Format("{0} {1}", AutomationMaster.WARNING_FLAG, message), MessageLevel.Abridged); yield return(StartCoroutine(Unifier(true, false, message, failureContext))); }
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); }