using System; using System.Linq; using System.Threading; using System.Threading.Tasks; using MatterHackers.Agg; using MatterHackers.Agg.Platform; using MatterHackers.Agg.UI; using MatterHackers.GuiAutomation; using MatterHackers.MatterControl.PartPreviewWindow; using MatterHackers.MatterControl.SlicerConfiguration; using NUnit.Framework; namespace MatterHackers.MatterControl.Tests.Automation { [TestFixture, Category("MatterControl.UI.Automation"), RunInApplicationDomain, Apartment(ApartmentState.STA)] public class SliceSetingsTests { [Test] public async Task RaftEnabledPassedToSliceEngine() { await MatterControlUtilities.RunTest((testRunner) => { testRunner.WaitForFirstDraw() .AddAndSelectPrinter() .AddTestAssetsToLibrary(new[] { "Rook.amf" }) .AddItemToBedplate("", "Row Item Rook") .SwitchToSliceSettings() .SelectSliceSettingsField(SettingsKey.create_raft) .WaitForReloadAll(() => testRunner.StartSlicing()) .WaitFor(() => MatterControlUtilities.CompareExpectedSliceSettingValueWithActualVaue("enableRaft", "True"), 10); // Call compare slice settings method here Assert.IsTrue(MatterControlUtilities.CompareExpectedSliceSettingValueWithActualVaue("enableRaft", "True")); return Task.CompletedTask; }, overrideWidth: 1224, overrideHeight: 800); } [Test] public async Task RelativeRetractionExecutesCorrectly() { await MatterControlUtilities.RunTest((testRunner) => { testRunner.WaitForName("Cancel Wizard Button"); using (var emulator = testRunner.LaunchAndConnectToPrinterEmulator("Other", "Other")) { var printer = testRunner.FirstPrinter(); var farthestE = 0.0; printer.Connection.LineReceived += (e, line) => { // make sure the extrusion never goes back very far Assert.Greater(printer.Connection.CurrentExtruderDestination, farthestE - 10); farthestE = Math.Max(farthestE, printer.Connection.CurrentExtruderDestination); }; testRunner.AddItemToBedplate() .StartPrint(printer) .WaitFor(() => printer.Connection.Printing, 60) // wait for the print to start .WaitFor(() => !printer.Connection.Printing, 60); // wait for the print to finish } return Task.CompletedTask; }, maxTimeToRun: 120); } [Test, Category("Emulator")] public async Task PauseOnLayerTest() { await MatterControlUtilities.RunTest((testRunner) => { testRunner.WaitForName("Cancel Wizard Button"); using (var emulator = testRunner.LaunchAndConnectToPrinterEmulator()) { var printer = testRunner.FirstPrinter(); testRunner.AddItemToBedplate() .StartPrint(printer, pauseAtLayers: "4;2;a;not;6") .WaitForLayerAndResume(printer, 2) .WaitForLayerAndResume(printer, 4) .WaitForLayerAndResume(printer, 6) .WaitForPrintFinished(printer); } return Task.CompletedTask; }, maxTimeToRun: 120); } [Test, Category("Emulator")] public async Task OemSettingsChangeOfferedToUserTest() { await MatterControlUtilities.RunTest(async (testRunner) => { using (var emulator = testRunner.LaunchAndConnectToPrinterEmulator()) { var printer = testRunner.FirstPrinter(); var expectedWarningName = ValidationErrors.SettingsUpdateAvailable + " Row"; // open the print menu and prove no oem message testRunner.OpenPrintPopupMenu(); Assert.IsFalse(testRunner.NameExists(expectedWarningName, 1)); // close the menu testRunner.ClickByName("PartPreviewContent") .WaitFor(() => !testRunner.NamedWidgetExists("Start Print Button")) // test again in case we have not downloaded the profile the first time .OpenPrintPopupMenu(); Assert.IsFalse(testRunner.NameExists(expectedWarningName, 1)); // close the menu testRunner.ClickByName("PartPreviewContent") .WaitFor(() => !testRunner.NamedWidgetExists("Start Print Button")); Assert.AreEqual(0, (await ProfileManager.GetChangedOemSettings(printer)).Count()); // change some oem settings printer.Settings.SetValue(SettingsKey.layer_height, ".213", printer.Settings.OemLayer); printer.Settings.SetValue(SettingsKey.first_layer_height, ".213", printer.Settings.OemLayer); Assert.AreEqual(2, (await ProfileManager.GetChangedOemSettings(printer)).Count()); // open menu again and check that warning is now visible testRunner.OpenPrintPopupMenu() .ClickByName(ValidationErrors.SettingsUpdateAvailable + " Button") .ClickByName(SettingsKey.layer_height + " Update"); Assert.AreEqual(1, (await ProfileManager.GetChangedOemSettings(printer)).Count()); testRunner.ClickByName("Cancel Wizard Button"); testRunner.OpenPrintPopupMenu(); Assert.IsTrue(testRunner.NameExists(expectedWarningName, 1)); // close the menu testRunner.ClickByName("PartPreviewContent") .WaitFor(() => !testRunner.NamedWidgetExists("Start Print Button")) // open the menu button .ClickByName("Printer Overflow Menu") .ClickByName("Update Settings... Menu Item") .ClickByName(SettingsKey.first_layer_height + " Update"); // accept the last option Assert.AreEqual(0, (await ProfileManager.GetChangedOemSettings(printer)).Count()); } }, maxTimeToRun: 120); } [Test, Category("Emulator")] public async Task MenuStaysOpenOnRebuildSettings() { await MatterControlUtilities.RunTest((testRunner) => { using (var emulator = testRunner.LaunchAndConnectToPrinterEmulator()) { var printer = testRunner.FirstPrinter(); // open the print menu and prove no oem message testRunner.OpenPrintPopupMenu(); var supportWidegtName = SettingsKey.create_per_layer_support.SettingWidgetName(); Assert.IsTrue(testRunner.NameExists(supportWidegtName, 1), "support option is visible"); // toggle supports var supportButton = testRunner.GetWidgetByName(supportWidegtName, out _) as ICheckbox; for (int i = 0; i < 3; i++) { testRunner.ClickByName(supportWidegtName) .WaitFor(() => supportButton.Checked) .ClickByName(supportWidegtName) .WaitFor(() => !supportButton.Checked); } Assert.IsTrue(testRunner.NameExists(supportWidegtName, 1), "Print menu should still be open after toggle supports"); } return Task.CompletedTask; }, maxTimeToRun: 120); } [Test, Category("Emulator")] public async Task SettingsStayOpenOnRebuildSettings() { await MatterControlUtilities.RunTest((testRunner) => { using (var emulator = testRunner.LaunchAndConnectToPrinterEmulator(pinSettingsOpen: false)) { var printer = testRunner.FirstPrinter(); testRunner.OpenSettingsSidebar(false); for (int i = 0; i < 3; i++) { testRunner.Delay() .ClickByName("Slice Settings Overflow Menu") .Delay() .ClickByName("Advanced Menu Item") .Delay() .ClickByName("Slice Settings Overflow Menu") .Delay() .ClickByName("Simple Menu Item"); } } return Task.CompletedTask; }, maxTimeToRun: 120); } [Test, Category("Emulator")] public async Task CancelWorksAsExpected() { await MatterControlUtilities.RunTest((testRunner) => { using (var emulator = testRunner.LaunchAndConnectToPrinterEmulator()) { Assert.AreEqual(1, ApplicationController.Instance.ActivePrinters.Count(), "One printer should exist after add"); var printer = testRunner.FirstPrinter(); printer.Settings.SetValue(SettingsKey.cancel_gcode, "G28 ; Cancel GCode"); testRunner.AddItemToBedplate() .StartPrint(printer, pauseAtLayers: "2") // Wait for the Ok button .WaitForName("Yes Button", 30); emulator.RunSlow = true; testRunner.ClickByName("Yes Button") // Cancel the Printing task .ClickByName("Stop Task Button") // Wait for and assert that printing has been canceled .WaitFor(() => printer.Connection.CommunicationState == PrinterCommunication.CommunicationStates.Connected); Assert.AreEqual(printer.Connection.CommunicationState, PrinterCommunication.CommunicationStates.Connected); // Assert that two G28s were output to the terminal int g28Count = printer.Connection.TerminalLog.AllLines().Where(line => line.Contains("G28")).Count(); Assert.AreEqual(2, g28Count, "The terminal log should contain one G28 from Start-GCode and one G28 from Cancel-GCode"); } return Task.CompletedTask; }, maxTimeToRun: 120); } [Test /* Test will fail if screen size is and "HeatBeforeHoming" falls below the fold */] public async Task ClearingCheckBoxClearsUserOverride() { await MatterControlUtilities.RunTest((testRunner) => { testRunner.WaitForFirstDraw() .AddAndSelectPrinter("Airwolf 3D", "HD") // Navigate to Local Library .SwitchToPrinterSettings() .ClickByName("Features Tab"); var printer = testRunner.FirstPrinter(); CheckAndUncheckSetting(testRunner, printer, SettingsKey.heat_extruder_before_homing, false); CheckAndUncheckSetting(testRunner, printer, SettingsKey.has_fan, true); return Task.CompletedTask; }, overrideWidth: 1224, overrideHeight: 900, maxTimeToRun: 600); } [Test] public async Task DualExtrusionShowsCorrectHotendData() { await MatterControlUtilities.RunTest((testRunner) => { using (var emulator = testRunner.LaunchAndConnectToPrinterEmulator()) { testRunner.ClickByName("Features Tab"); // only 1 hotend and 1 extruder Assert.IsTrue(testRunner.NameExists("Hotend 0")); Assert.IsTrue(testRunner.NameExists("Bed TemperatureWidget")); Assert.IsFalse(testRunner.NameExists("Hotend 1", .1)); testRunner.ClickByName("Hotend 0"); // assert the temp is set when we first open (it comes from the material) MHNumberEdit tempWidget = testRunner.GetWidgetByName("Temperature Input", out _) as MHNumberEdit; Assert.AreEqual(240, (int)tempWidget.Value); // change material var dropDownLists = testRunner.GetWidgetsByName("Hotend Preset Selector"); Assert.AreEqual(1, dropDownLists.Count, "There is one. The slice settings and the pop out."); DropDownList materialSelector = dropDownLists[0].Widget as DropDownList; Assert.AreEqual("", materialSelector.SelectedValue); testRunner.ClickByName("Hotend Preset Selector") .ClickByName("HIPS Menu"); // check the extruder count var extrudeButtons = testRunner.GetWidgetsByName("Extrude Button"); Assert.AreEqual(1, extrudeButtons.Count, "There should be just one."); int hipsGoalTemp = 220; testRunner.Delay(); // assert the temp changed to a new temp Assert.AreEqual(hipsGoalTemp,(int) tempWidget.Value, "The goal temp should match the material temp"); // and the printer heat is off Assert.AreEqual(0, (int) emulator.CurrentExtruder.TargetTemperature, "The printer should report the heaters are off"); // turn on the heater testRunner.ClickByName("Toggle Heater") .Delay(1); // assert the printer is heating Assert.AreEqual(hipsGoalTemp, (int)emulator.CurrentExtruder.TargetTemperature, "The printer should report the expected goal temp"); // turn off the heater testRunner.ClickByName("Toggle Heater") .Delay(1); // assert the printer is off Assert.AreEqual(0, (int)emulator.CurrentExtruder.TargetTemperature, "The printer should report the heaters are off"); // type in a temp when the heating is off testRunner.ClickByName("Temperature Input") .Type("110") .Type("{Enter}") .Delay(); // assert the printer is off Assert.AreEqual(0, (int)emulator.CurrentExtruder.TargetTemperature); // and the heat toggle is showing on ICheckbox heatToggle = testRunner.GetWidgetByName("Toggle Heater", out _) as ICheckbox; Assert.IsFalse(heatToggle.Checked); // turn it on testRunner.ClickByName("Toggle Heater"); Assert.AreEqual(110, (int)emulator.CurrentExtruder.TargetTemperature); // adjust when on testRunner.ClickByName("Temperature Input") .Type("104") .Type("{Enter}") .Delay(); Assert.AreEqual(104, (int)emulator.CurrentExtruder.TargetTemperature); // type in 0 and have the heater turn off testRunner.ClickByName("Temperature Input") .Type("^a") .Type("0") .Type("{Enter}") .Delay() // type in 60 and have the heater turn on .ClickByName("Temperature Input") .Type("^a") .Type("60") .Type("{Enter}") .Delay() .ClickByName("Toggle Heater"); Assert.AreEqual(60, (int)emulator.CurrentExtruder.TargetTemperature); // click the remove override and have it change to default temp testRunner.ClickByName("Restore temperature") .WaitFor(() => hipsGoalTemp == emulator.CurrentExtruder.TargetTemperature); Assert.AreEqual(hipsGoalTemp, (int)emulator.CurrentExtruder.TargetTemperature, "The printer should report the expected goal temp"); // type in 60 and have the heater turn on testRunner.ClickByName("Temperature Input") .Type("^a") .Type("60") .Type("{Enter}") .Delay(); Assert.AreEqual(60, (int)emulator.CurrentExtruder.TargetTemperature); // type in 0 and have the heater turn off testRunner.ClickByName("Temperature Input") .Type("^a") .Type("0") .Type("{Enter}") .Delay(); // assert the printer is not heating Assert.AreEqual(0, (int)emulator.CurrentExtruder.TargetTemperature); // and the on toggle is showing off Assert.IsFalse(heatToggle.Checked); // test that the load filament button works and closes correctly testRunner.ClickByName("Temperature Input") .Type("^a") .Type("104") .Type("{Enter}") .Delay() .ClickByName("Load Filament Button") .ClickByName("Load Filament"); Assert.AreEqual(104, (int)emulator.CurrentExtruder.TargetTemperature); testRunner.Delay() .ClickByName("Cancel Wizard Button") .Delay(); Assert.AreEqual(0, (int)emulator.CurrentExtruder.TargetTemperature); testRunner.ClickByName("Hotend 0") .ClickByName("Load Filament Button") .ClickByName("Load Filament") .Delay(); Assert.AreEqual(104, (int)emulator.CurrentExtruder.TargetTemperature); var systemWindow = testRunner.GetWidgetByName("Cancel Wizard Button", out SystemWindow containingWindow); // close the window through windows (alt-f4) testRunner.Type("%{F4}"); Assert.AreEqual(0, (int)emulator.CurrentExtruder.TargetTemperature); // Switch back to the general tab testRunner.ClickByName("General Tab") .SelectSliceSettingsField(SettingsKey.extruder_count) .Type("2") .Type("{Enter}"); // there are now 2 hotends and 2 extruders Assert.IsTrue(testRunner.NameExists("Hotend 0")); Assert.IsTrue(testRunner.NameExists("Hotend 1")); var printer = testRunner.FirstPrinter(); SetCheckBoxSetting(testRunner, printer, SettingsKey.extruders_share_temperature, true); // there is one hotend and 2 extruders Assert.IsTrue(testRunner.NameExists("Hotend 0")); Assert.IsFalse(testRunner.NameExists("Hotend 1", .1)); testRunner.ClickByName("Hotend 0"); extrudeButtons = testRunner.GetWidgetsByName("Extrude Button"); Assert.AreEqual(2, extrudeButtons.Count, "Now there should be two."); } return Task.CompletedTask; }, maxTimeToRun: 120); } [Test] public void SliceSettingsOrganizerSupportsKeyLookup() { StaticData.RootPath = TestContext.CurrentContext.ResolveProjectPath(5, "MatterControl", "StaticData"); var organizer = PrinterSettings.Layout; var userLevel = organizer.AllSliceSettings; Assert.IsNotNull(userLevel); // Confirm expected keys Assert.IsTrue(userLevel.ContainsKey("bed_temperature")); Assert.IsTrue(organizer.AllSliceSettings.ContainsKey("bed_temperature")); Assert.IsTrue(organizer.AllPrinterSettings.ContainsKey("extruder_count")); // Confirm non-existent key Assert.IsFalse(userLevel.ContainsKey("non_existing_setting")); Assert.IsFalse(organizer.AllSliceSettings.ContainsKey("non_existing_setting")); } [Test /* Test will fail if screen size is and "HeatBeforeHoming" falls below the fold */] public async Task SwitchingMaterialsCausesSettingsChangedEvents() { await MatterControlUtilities.RunTest((testRunner) => { testRunner.AddAndSelectPrinter(); var printer = testRunner.FirstPrinter(); printer.Settings.OemLayer[SettingsKey.layer_height] = ".2"; int layerHeightChangedCount = 0; PrinterSettings.AnyPrinterSettingChanged += (s, stringEvent) => { if (stringEvent != null) { if (stringEvent.Data == SettingsKey.layer_height) { layerHeightChangedCount++; } } }; // Navigate to Local Library testRunner.SwitchToSliceSettings(); // Navigate to General Tab -> Layers / Surface Tab testRunner.SelectSliceSettingsField(SettingsKey.layer_height); Assert.AreEqual(0, layerHeightChangedCount, "No change to layer height yet."); var theme = ApplicationController.Instance.Theme; var indicator = testRunner.GetWidgetByName("Layer Thickness OverrideIndicator", out _); Assert.AreEqual(Color.Transparent, indicator.BackgroundColor); testRunner.ClickByName("Quality") .ClickByName("Fine Menu") .Delay(.5); Assert.AreEqual(1, layerHeightChangedCount, "Changed to fine."); Assert.AreEqual(theme.PresetColors.QualityPreset, indicator.BackgroundColor); testRunner.ClickByName("Quality") .ClickByName("Standard Menu") .Delay(.5); Assert.AreEqual(2, layerHeightChangedCount, "Changed to standard."); Assert.AreEqual(theme.PresetColors.QualityPreset, indicator.BackgroundColor); testRunner.ClickByName("Quality") .ClickByName("- none - Menu Item") .Delay(.5); Assert.AreEqual(Color.Transparent, indicator.BackgroundColor); Assert.AreEqual(3, layerHeightChangedCount, "Changed to - none -."); testRunner.ClickByName("Quality") .ClickByName("Standard Menu") .Delay(.5); Assert.AreEqual(4, layerHeightChangedCount, "Changed to standard."); Assert.AreEqual(theme.PresetColors.QualityPreset, indicator.BackgroundColor); // TODO: delete one of the settings // asserts that the deleted setting has been removed from the list return Task.CompletedTask; }, maxTimeToRun: 1000); } [Test] public async Task DeleteProfileWorksForGuest() { await MatterControlUtilities.RunTest((testRunner) => { testRunner.WaitForFirstDraw(); // assert no profiles Assert.AreEqual(0, ProfileManager.Instance.ActiveProfiles.Count()); testRunner.AddAndSelectPrinter("Airwolf 3D", "HD"); // assert one profile Assert.AreEqual(1, ProfileManager.Instance.ActiveProfiles.Count(), "One profile should exist after add"); MatterControlUtilities.DeleteSelectedPrinter(testRunner); // assert no profiles Assert.AreEqual(0, ProfileManager.Instance.ActiveProfiles.Count(), "No profiles should exist after delete"); return Task.CompletedTask; }, overrideWidth: 1224, overrideHeight: 900); } private static void SetCheckBoxSetting(AutomationRunner testRunner, PrinterConfig printer, string settingToChange, bool valueToSet) { var settingsData = PrinterSettings.SettingsData[settingToChange]; string checkBoxName = $"{settingsData.PresentationName} Field"; Assert.IsTrue(printer.Settings.GetValue(settingToChange) != valueToSet); //testRunner.ScrollIntoView(checkBoxName); //testRunner.ClickByName(checkBoxName); testRunner.SelectSliceSettingsField(settingToChange) // give some time for the ui to update if necessary .Delay(2); Assert.IsTrue(printer.Settings.GetValue(settingToChange) == valueToSet); } private static void CheckAndUncheckSetting(AutomationRunner testRunner, PrinterConfig printer, string settingToChange, bool expected) { // Assert that the checkbox is currently unchecked, and there is no user override Assert.IsFalse(printer.Settings.UserLayer.ContainsKey(settingToChange)); // Click the checkbox SetCheckBoxSetting(testRunner, printer, settingToChange, !expected); // Assert the checkbox is checked and the user override is set Assert.IsTrue(printer.Settings.UserLayer.ContainsKey(settingToChange)); // make sure the setting is still open in case of a reload all testRunner.NavigateToSliceSettingsField(settingToChange) // Click the cancel user override button .ClickByName("Restore " + settingToChange) .Delay(2); // Assert the checkbox is unchecked and there is no user override Assert.IsTrue(printer.Settings.GetValue(settingToChange) == expected); Assert.IsFalse(printer.Settings.UserLayer.ContainsKey(settingToChange)); } [Test] public async Task HasHeatedBedCheckedHidesBedTemperatureOptions() { await MatterControlUtilities.RunTest((testRunner) => { testRunner.WaitForFirstDraw() .AddAndSelectPrinter("Airwolf 3D", "HD") // Navigate to Settings Tab and make sure Bed Temp Text box is visible .SwitchToSliceSettings() .SelectSliceSettingsField(SettingsKey.bed_temperature) .SelectSliceSettingsField(SettingsKey.temperature) // Uncheck Has Heated Bed checkbox and make sure Bed Temp Textbox is not visible .SwitchToPrinterSettings() .SelectSliceSettingsField(SettingsKey.has_heated_bed) .Delay(.5) .SwitchToSliceSettings() .NavigateToSliceSettingsField(SettingsKey.temperature); Assert.IsFalse(testRunner.WaitForName("Bed Temperature Textbox", .5), "Filament -> Bed Temp should not be visible after Heated Bed unchecked"); // Make sure Bed Temperature Options are not visible in printer controls testRunner.SwitchToControlsTab(); Assert.IsFalse(testRunner.WaitForName("Bed Temperature Controls Widget", .5), "Controls -> Bed Temp should not be visible after Heated Bed unchecked"); return Task.CompletedTask; }); } [Test] public async Task QualitySettingsStayAsOverrides() { await MatterControlUtilities.RunTest((testRunner) => { testRunner.WaitForFirstDraw(); // Add Guest printers testRunner.AddAndSelectPrinter("Airwolf 3D", "HD") .SwitchToSliceSettings(); var printer = testRunner.FirstPrinter(); testRunner.SelectSliceSettingsField(SettingsKey.layer_height) .Type(".5") // Force lose focus .SelectSliceSettingsField(SettingsKey.first_layer_height) .WaitFor(() => printer.Settings.GetValue(SettingsKey.layer_height) == 0.5); Assert.AreEqual(printer.Settings.GetValue(SettingsKey.layer_height).ToString(), "0.5", "Layer height is what we set it to"); testRunner.ClickByName("Quality") .ClickByName("Fine Menu"); testRunner.WaitFor(() => printer.Settings.GetValue(SettingsKey.layer_height) == 0.1); Assert.AreEqual(printer.Settings.GetValue(SettingsKey.layer_height).ToString(), "0.1", "Layer height is the fine override"); // Close Airwolf testRunner.CloseFirstPrinterTab(); // Assert printer counts Assert.AreEqual(1, ProfileManager.Instance.ActiveProfiles.Count(), "ProfileManager should have 1 profile after Airwolf close"); Assert.AreEqual(0, ApplicationController.Instance.ActivePrinters.Count(), "Zero printers should be active after Airwolf close"); testRunner.AddAndSelectPrinter("BCN3D", "Sigma"); // Assert printer counts Assert.AreEqual(2, ProfileManager.Instance.ActiveProfiles.Count(), "ProfileManager has 2 profiles"); Assert.AreEqual(1, ApplicationController.Instance.ActivePrinters.Count(), "One printer should be active after BCN add"); // Close BCN testRunner.CloseFirstPrinterTab() // Reopen Airwolf .SwitchToHardwareTab() .DoubleClickByName("Airwolf 3D HD Node") .Delay(0.2); printer = testRunner.FirstPrinter(); testRunner.WaitFor(() => printer.Settings.GetValue(SettingsKey.layer_height) == 0.1); Assert.AreEqual(printer.Settings.GetValue(SettingsKey.layer_height).ToString(), "0.1", "Layer height is the fine override"); // Switch to Slice Settings Tab testRunner.ClickByName("Slice Settings Tab") .ClickByName("Quality") .ClickByName("- none - Menu Item") .WaitFor(() => printer.Settings.GetValue(SettingsKey.layer_height) == 0.5); Assert.AreEqual(printer.Settings.GetValue(SettingsKey.layer_height).ToString(), "0.5", "Layer height is what we set it to"); return Task.CompletedTask; }, maxTimeToRun: 120); } [Test] public void CopyFromTest() { StaticData.RootPath = TestContext.CurrentContext.ResolveProjectPath(4, "StaticData"); MatterControlUtilities.OverrideAppDataLocation(TestContext.CurrentContext.ResolveProjectPath(4)); var settings = new PrinterSettings(); settings.ID = "12345-print"; settings.SetValue(SettingsKey.auto_connect, "1"); settings.SetValue(SettingsKey.com_port, "some_com_port"); settings.SetValue(SettingsKey.cancel_gcode, "hello world"); settings.Macros.Add(new GCodeMacro() { Name = "Macro1", GCode = "G28 ; Home Printer" }); settings.StagedUserSettings["retract_restart_extra_time_to_apply"] = "4"; settings.StagedUserSettings["retract_restart_extra"] = "0.3"; settings.StagedUserSettings["bridge_fan_speed"] = "50"; var sha1 = settings.ComputeSHA1(); var clone = new PrinterSettings(); clone.CopyFrom(settings); Assert.AreEqual(settings.ToJson(), clone.ToJson(), "Cloned settings via CopyFrom should equal source"); Assert.AreEqual(sha1, clone.ComputeSHA1(), "Cloned settings via CopyFrom should equal source"); } private void CloseAllPrinterTabs(AutomationRunner testRunner) { // Close all printer tabs var mainViewWidget = testRunner.GetWidgetByName("PartPreviewContent", out _) as MainViewWidget; foreach (var tab in mainViewWidget.TabControl.AllTabs.Where(t => t.TabContent is PrinterTabPage).ToList()) { if (tab is GuiWidget widget) { var closeWidget = widget.Descendants().First(); closeWidget.InvokeClick(); } } } } }