Move MatterControl source code into a subdirectory

This commit is contained in:
Nettika 2026-01-28 21:30:58 -08:00
parent 2c6e34243a
commit 70af2d9ae8
Signed by: nettika
SSH key fingerprint: SHA256:f+PJrfIq49zrQ6dQrHj18b+PJKmAldeAMiGdj8IzXCA
2007 changed files with 13 additions and 8 deletions

View file

@ -0,0 +1,195 @@
using MatterHackers.Agg;
using MatterHackers.DataConverters3D;
using MatterHackers.GuiAutomation;
using MatterHackers.MatterControl.PartPreviewWindow;
using MatterHackers.VectorMath;
using NUnit.Framework;
using System;
using System.Threading;
using System.Threading.Tasks;
using TestInvoker;
namespace MatterHackers.MatterControl.Tests.Automation
{
[TestFixture, Category("MatterControl.UI.Automation"), Parallelizable(ParallelScope.Children)]
public class CameraFittingUtilTests
{
private const string CoinName = "MatterControl - Coin.stl";
static Task DoZoomToSelectionTest(bool ortho, bool wideObject)
{
return MatterControlUtilities.RunTest(testRunner =>
{
testRunner.OpenPartTab(removeDefaultPhil: wideObject);
var view3D = testRunner.GetWidgetByName("View3DWidget", out _) as View3DWidget;
var scene = view3D.Object3DControlLayer.Scene;
if (wideObject)
AddCoinToBed(testRunner, scene);
if (ortho)
{
testRunner.ClickByName("Projection mode button");
Assert.IsTrue(!view3D.TrackballTumbleWidget.PerspectiveMode);
testRunner.Delay(1);
Assert.IsTrue(!view3D.TrackballTumbleWidget.PerspectiveMode);
}
else
{
Assert.IsTrue(view3D.TrackballTumbleWidget.PerspectiveMode);
}
Vector3[] lookAtDirFwds = new Vector3[] {
new Vector3(0, 0, -1),
new Vector3(0, 0, 1),
new Vector3(0, 1, 0),
new Vector3(1, 1, 0),
new Vector3(-1, -1, 0),
new Vector3(0, 1, 1),
new Vector3(1, 1, 1),
new Vector3(0, 1, -1),
new Vector3(1, 1, -1),
};
const int topI = 0;
const int bottomI = 1;
for (int i = 0; i < lookAtDirFwds.Length; ++i)
{
Vector3 lookAtDirFwd = lookAtDirFwds[i];
Vector3 lookAtDirRight = (i == topI ? -Vector3.UnitY : i == bottomI ? Vector3.UnitY : -Vector3.UnitZ).Cross(lookAtDirFwd);
Vector3 lookAtDirUp = lookAtDirRight.Cross(lookAtDirFwd).GetNormal();
var look = Matrix4X4.LookAt(Vector3.Zero, lookAtDirFwd, lookAtDirUp);
view3D.TrackballTumbleWidget.AnimateRotation(look);
testRunner.Delay(0.5);
testRunner.ClickByName("Zoom to selection button");
testRunner.Delay(0.5);
var part = testRunner.GetObjectByName(wideObject ? CoinName : "Phil A Ment.stl", out _) as IObject3D;
AxisAlignedBoundingBox worldspaceAABB = part.GetAxisAlignedBoundingBox();
Vector2 viewportSize = new Vector2(view3D.TrackballTumbleWidget.Width, view3D.TrackballTumbleWidget.Height);
RectangleDouble rect = view3D.TrackballTumbleWidget.WorldspaceAabbToBottomScreenspaceRectangle(worldspaceAABB);
Vector2 screenspacePositionOfWorldspaceCenter = view3D.TrackballTumbleWidget.WorldspaceToBottomScreenspace(worldspaceAABB.Center).Xy;
double marginPixels = CameraFittingUtil.MarginScale * Math.Min(viewportSize.X, viewportSize.Y);
const double pixelTolerance = 1e-3;
// Check that the full object is visible.
Assert.IsTrue(rect.Left > -pixelTolerance);
Assert.IsTrue(rect.Bottom > -pixelTolerance);
Assert.IsTrue(rect.Right < viewportSize.X + pixelTolerance);
Assert.IsTrue(rect.Top < viewportSize.Y + pixelTolerance);
// Check for centering.
bool isPerspectiveFittingWithinMargin =
CameraFittingUtil.PerspectiveFittingAlgorithm == CameraFittingUtil.EPerspectiveFittingAlgorithm.Sphere ||
CameraFittingUtil.PerspectiveFittingAlgorithm == CameraFittingUtil.EPerspectiveFittingAlgorithm.CenterOnWorldspaceAABB ||
CameraFittingUtil.PerspectiveFittingAlgorithm == CameraFittingUtil.EPerspectiveFittingAlgorithm.CenterOnViewspaceAABB ||
CameraFittingUtil.PerspectiveFittingAlgorithm == CameraFittingUtil.EPerspectiveFittingAlgorithm.IntersectionOfBoundingPlanesWithApproxCentering ||
CameraFittingUtil.PerspectiveFittingAlgorithm == CameraFittingUtil.EPerspectiveFittingAlgorithm.IntersectionOfBoundingPlanesWithPerfectCentering;
// Tightly bounded. At least one axis should be bounded by the margin.
bool isPerspectiveFittingBoundedByMargin =
CameraFittingUtil.PerspectiveFittingAlgorithm == CameraFittingUtil.EPerspectiveFittingAlgorithm.IntersectionOfBoundingPlanesWithApproxCentering ||
CameraFittingUtil.PerspectiveFittingAlgorithm == CameraFittingUtil.EPerspectiveFittingAlgorithm.IntersectionOfBoundingPlanesWithPerfectCentering;
bool perspectiveFittingWillCenterTheAABBCenter =
CameraFittingUtil.PerspectiveFittingAlgorithm == CameraFittingUtil.EPerspectiveFittingAlgorithm.Sphere ||
CameraFittingUtil.PerspectiveFittingAlgorithm == CameraFittingUtil.EPerspectiveFittingAlgorithm.CenterOnWorldspaceAABB ||
CameraFittingUtil.PerspectiveFittingAlgorithm == CameraFittingUtil.EPerspectiveFittingAlgorithm.CenterOnViewspaceAABB;
bool perspectiveFittingWillCenterTheScreenspaceAABB =
CameraFittingUtil.PerspectiveFittingAlgorithm == CameraFittingUtil.EPerspectiveFittingAlgorithm.IntersectionOfBoundingPlanesWithPerfectCentering;
// Always get the same result.
bool isPerspectiveFittingStable =
CameraFittingUtil.PerspectiveFittingAlgorithm != CameraFittingUtil.EPerspectiveFittingAlgorithm.TrialAndError;
bool isXWorldspaceCentered = MathHelper.AlmostEqual(viewportSize.X / 2, screenspacePositionOfWorldspaceCenter.X, pixelTolerance);
bool isYWorldspaceCentered = MathHelper.AlmostEqual(viewportSize.Y / 2, screenspacePositionOfWorldspaceCenter.Y, pixelTolerance);
bool isXMarginBounded = MathHelper.AlmostEqual(rect.Left, marginPixels, 1e-3) && MathHelper.AlmostEqual(rect.Right, viewportSize.X - marginPixels, pixelTolerance);
bool isYMarginBounded = MathHelper.AlmostEqual(rect.Bottom, marginPixels, 1e-3) && MathHelper.AlmostEqual(rect.Top, viewportSize.Y - marginPixels, pixelTolerance);
bool isXWithinMargin = rect.Left > marginPixels - 1 && rect.Right < viewportSize.X - (marginPixels - 1);
bool isYWithinMargin = rect.Bottom > marginPixels - 1 && rect.Top < viewportSize.Y - (marginPixels - 1);
bool isXScreenspaceCentered = MathHelper.AlmostEqual(viewportSize.X / 2, (rect.Left + rect.Right) / 2, pixelTolerance);
bool isYScreenspaceCentered = MathHelper.AlmostEqual(viewportSize.Y / 2, (rect.Bottom + rect.Top) / 2, pixelTolerance);
if (ortho)
{
// Ortho fitting will always center the screenspace AABB and the center of the object AABB.
Assert.IsTrue(isXWorldspaceCentered && isYWorldspaceCentered);
Assert.IsTrue(isXMarginBounded || isYMarginBounded);
Assert.IsTrue(isXWithinMargin && isYWithinMargin);
Assert.IsTrue(isXScreenspaceCentered && isYScreenspaceCentered);
}
else
{
if (isPerspectiveFittingWithinMargin)
Assert.IsTrue(isXWithinMargin && isYWithinMargin);
if (isPerspectiveFittingBoundedByMargin)
Assert.IsTrue(isXMarginBounded || isYMarginBounded);
if (perspectiveFittingWillCenterTheAABBCenter)
Assert.IsTrue(isXWorldspaceCentered && isYWorldspaceCentered);
if (perspectiveFittingWillCenterTheScreenspaceAABB)
Assert.IsTrue(isXScreenspaceCentered && isYScreenspaceCentered);
}
if (ortho || isPerspectiveFittingStable)
{
testRunner.ClickByName("Zoom to selection button");
testRunner.Delay(1);
RectangleDouble rect2 = view3D.TrackballTumbleWidget.WorldspaceAabbToBottomScreenspaceRectangle(worldspaceAABB);
Assert.IsTrue(rect2.Equals(rect, pixelTolerance));
}
}
return Task.CompletedTask;
}, maxTimeToRun: 60 * 3, overrideWidth: 1300, overrideHeight: 800);
}
[Test, ChildProcessTest]
public Task OrthographicZoomToSelectionWide()
{
return DoZoomToSelectionTest(true, true);
}
[Test, ChildProcessTest]
public Task OrthographicZoomToSelectionTall()
{
return DoZoomToSelectionTest(true, false);
}
[Test, ChildProcessTest]
public Task PerspectiveZoomToSelectionWide()
{
return DoZoomToSelectionTest(false, true);
}
[Test, ChildProcessTest]
public Task PerspectiveZoomToSelectionTall()
{
return DoZoomToSelectionTest(false, false);
}
private static void AddCoinToBed(AutomationRunner testRunner, InteractiveScene scene)
{
testRunner.AddItemToBed(partName: "Row Item MatterControl - Coin.stl")
.Delay(.1)
.ClickByName(CoinName, offset: new Point2D(-4, 0));
Assert.IsNotNull(scene.SelectedItem);
}
}
}

View file

@ -0,0 +1,66 @@
/*
Copyright (c) 2014, Lars Brubaker
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the FreeBSD Project.
*/
using MatterHackers.Agg.UI;
using NUnit.Framework;
using System.Threading.Tasks;
using TestInvoker;
namespace MatterHackers.MatterControl.Tests.Automation
{
[TestFixture, Category("MatterControl.UI.Automation")]
public class CreateLibraryFolder
{
[Test, ChildProcessTest]
public async Task CreateFolderStartsWithTextFieldFocusedAndEditable()
{
await MatterControlUtilities.RunTest((testRunner) =>
{
testRunner.OpenPartTab();
testRunner.NavigateToFolder("Local Library Row Item Collection");
testRunner.InvokeLibraryCreateFolderDialog();
testRunner.Delay(.5);
testRunner.Type("Test Text");
testRunner.Delay(.5);
var textWidgetMH = testRunner.GetWidgetByName("InputBoxPage TextEditWidget", out _) as ThemedTextEditWidget;
Assert.IsTrue(textWidgetMH != null, "Found Text Widget");
Assert.IsTrue(textWidgetMH.Text == "Test Text", "Had the right text");
testRunner.ClickByName("Cancel Wizard Button");
testRunner.Delay(.5);
return Task.CompletedTask;
});
}
}
}

View file

@ -0,0 +1,265 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using MatterHackers.Agg;
using MatterHackers.Agg.Platform;
using MatterHackers.Agg.UI;
using MatterHackers.DataConverters3D;
using MatterHackers.GuiAutomation;
using MatterHackers.MatterControl.DesignTools;
using MatterHackers.MatterControl.DesignTools.Operations;
using MatterHackers.MatterControl.PartPreviewWindow;
using MatterHackers.VectorMath;
using NUnit.Framework;
using TestInvoker;
namespace MatterHackers.MatterControl.Tests.Automation
{
[TestFixture]
public class SheetDataTests
{
[Test]
public void Calculations()
{
var sheetData = new SheetData(4, 4);
void Test(string cell, string expression, string expected)
{
sheetData[cell].Expression = expression;
sheetData.Recalculate();
Assert.AreEqual(expected, sheetData.GetCellValue(cell));
}
// simple multiply retrived upper and lower case
Test("A1", "=4*2", "8");
Test("a1", "=4*2", "8");
// make sure functions are working, max in this case
Test("a2", "=max(4, 5)", "5");
// make sure cell references are working
Test("a3", "=a1+a2", "13");
// complex formulas are working
Test("a4", "=((4+5)/3+7)/5", "2");
// complex formulas with references are working
Test("b1", "=(a4+a3)*.5", "7.5");
// constants work, like pi
Test("b2", "=pi", "3.141592653589793");
// check that we get string data back unmodified
Test("b3", "hello", "hello");
}
}
[TestFixture, Category("MatterControl.UI.Automation"), Parallelizable(ParallelScope.Children)]
public class PrimitiveAndSheetsTests
{
[SetUp]
public void TestSetup()
{
StaticData.RootPath = MatterControlUtilities.StaticDataPath;
MatterControlUtilities.OverrideAppDataLocation(MatterControlUtilities.RootPath);
}
[Test, ChildProcessTest]
public void SheetEditorLayoutAndNavigation()
{
var systemWindow = new SystemWindow(800, 600)
{
Name = "Main Window",
};
InternalTextEditWidget.AddTextWidgetRightClickMenu(ApplicationController.Instance.MenuTheme);
AutomationRunner.TimeToMoveMouse = .1;
var sheetData = new SheetData(5, 5);
var undoBuffer = new UndoBuffer();
var theme = ApplicationController.Instance.Theme;
var sheetEditor = new SheetEditorWidget(sheetData, null, undoBuffer, theme);
systemWindow.AddChild(sheetEditor);
AutomationRunner.ShowWindowAndExecuteTests(systemWindow, testRunner =>
{
testRunner.Delay(1);
return Task.CompletedTask;
},
2000);
}
[Test, ChildProcessTest]
public async Task DimensionsWorkWhenNoSheet()
{
await MatterControlUtilities.RunTest((testRunner) =>
{
testRunner.OpenPartTab();
var primitive = "Cube";
var primitiveName = "Row Item " + primitive;
testRunner.DoubleClickByName(primitiveName);
// Get View3DWidget
View3DWidget view3D = testRunner.GetWidgetByName("View3DWidget", out var window, 3) as View3DWidget;
var scene = view3D.Object3DControlLayer.Scene;
testRunner.WaitForName(primitive);
Assert.AreEqual(1, scene.Children.Count, "Should have 1 part");
var cube = testRunner.GetObjectByName(primitive, out _) as CubeObject3D;
Assert.AreEqual(20, cube.Width.Value(cube), .001);
// Select scene object
testRunner.Select3DPart(primitive);
// Scale it wider
testRunner.DragDropByName("ScaleWidthRight",
"ScaleWidthRight",
offsetDrag: new Point2D(0, 0),
offsetDrop: new Point2D(0, 10));
Assert.Greater(cube.Width.Value(cube), 20.0);
testRunner.ClickByName("3D View Undo");
Assert.AreEqual(20, cube.Width.Value(cube), .0001);
// try scaling by text entry
testRunner.ClickByName("ScaleWidthLeft")
.ClickByName("XValueDisplay")
//.Assert(() => testRunner.GetWidgetByName("XValueDisplay", out var _).ContainsFocus, "Focus") // Sometimes, when the moves over to XValueDisplay, XValueDisplay just isn't there.
.Type("35")
//.Assert(() => ((CustomWidgets.InlineEditControl)testRunner.GetWidgetByName("XValueDisplay", out var _)).Value == 35, "text")
.Type("{Enter}");
//.WaitFor(() => 35 == cube.Width.Value(cube));
/*if (35 != cube.Width.Value(cube))
{
System.Diagnostics.Debugger.Launch();
System.Diagnostics.Debugger.Break();
}*/
// NOTE: Happened once: Expected: 35, But was: 20.0d
// This can happen when running only this test, and alt-tabbing frequently.
// Failure again after platform focus fix.
// This can happen when running only this test, and not alt-tabbing frequently.
// Failure can happen in original .NET Framework MatterControl testing too.
// Possible cause is OnMouseMove(MatterHackers.Agg.UI.MouseEventArgs) (MatterHackers.MatterControl.PartPreviewWindow.Object3DControlsLayer) not updating the hover control in time.
Assert.AreEqual(35, cube.Width.Value(cube));
testRunner.ClickByName("3D View Undo");
Assert.AreEqual(20, cube.Width.Value(cube), .0001);
// try scaling by text entry of an equation
testRunner.ClickByName("Width Field")
.Type("=40 + 5")
.Type("{Enter}");
Assert.AreEqual(45, cube.Width.Value(cube));
// Select Nothing
testRunner.ClickByName("View3DWidget");
testRunner.SelectNone();
Assert.AreEqual(null, scene.SelectedItem);
// and re-select the object
testRunner.SelectAll();
Assert.AreEqual(1, scene.Children.Count);
Assert.AreEqual(cube, scene.SelectedItem);
// now that has an equation in the width it should not have an x edge controls
Assert.IsFalse(testRunner.NameExists("ScaleWidthRight", .2));
testRunner.ClickByName("3D View Undo");
Assert.AreEqual(20, cube.Width.Value(cube), .0001);
return Task.CompletedTask;
}, overrideWidth: 1300, maxTimeToRun: 60);
}
[Test, ChildProcessTest]
public async Task ScaleObjectWorksWithAndWithoutSheet()
{
await MatterControlUtilities.RunTest((testRunner) =>
{
testRunner.OpenPartTab();
var primitive = "Cube";
var primitiveName = "Row Item " + primitive;
testRunner.DoubleClickByName(primitiveName);
// Get View3DWidget
View3DWidget view3D = testRunner.GetWidgetByName("View3DWidget", out _, 3) as View3DWidget;
var scene = view3D.Object3DControlLayer.Scene;
testRunner.WaitForName(primitive);
Assert.AreEqual(1, scene.Children.Count, "Should have 1 part");
var cube = testRunner.GetObjectByName(primitive, out _) as CubeObject3D;
Assert.AreEqual(20, cube.Width.Value(cube));
// Select scene object and add a scale
testRunner.Select3DPart(primitive)
.ClickByName("Scale Inner SplitButton")
.ClickByName("Width Edit")
.Type("25")
.Type("{Enter}")
.Delay();
var scale = testRunner.GetObjectByName("Scale", out _) as ScaleObject3D_3;
var scaleAabb = scale.GetAxisAlignedBoundingBox(Matrix4X4.Identity);
Assert.AreEqual(25, scaleAabb.XSize);
// add a scale to the object
return Task.CompletedTask;
}, overrideWidth: 1300, maxTimeToRun: 60);
}
[Test, ChildProcessTest]
public void SheetEditorNavigationTests()
{
var systemWindow = new SystemWindow(800, 600)
{
Name = "Main Window",
};
InternalTextEditWidget.AddTextWidgetRightClickMenu(ApplicationController.Instance.MenuTheme);
AutomationRunner.TimeToMoveMouse = .1;
var theme = ApplicationController.Instance.Theme;
var container = new FlowLayoutWidget(FlowDirection.TopToBottom)
{
HAnchor = HAnchor.Stretch,
VAnchor = VAnchor.Stretch,
};
systemWindow.AddChild(container);
var sheetData = new SheetData(5, 5);
var undoBuffer = new UndoBuffer();
var sheetEditorWidget = new SheetEditorWidget(sheetData, null, undoBuffer, theme);
container.AddChild(sheetEditorWidget);
systemWindow.RunTest(testRunner =>
{
//testRunner.Delay(60);
return Task.CompletedTask;
},
2000);
}
}
public static class RunnerX
{
public static Task RunTest(this SystemWindow systemWindow, AutomationTest automationTest, int timeout)
{
return AutomationRunner.ShowWindowAndExecuteTests(systemWindow, automationTest, timeout);
}
}
}

View file

@ -0,0 +1,208 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using NUnit.Framework;
using System;
using TestInvoker;
namespace MatterHackers.MatterControl.Tests.Automation
{
[TestFixture, Category("MatterControl.UI.Automation")]
public class ExportGcodeFromExportWindow
{
[Test, ChildProcessTest]
public async Task ExportAsGcode()
{
await MatterControlUtilities.RunTest(testRunner =>
{
testRunner.WaitForFirstDraw()
.AddAndSelectPrinter("Airwolf 3D", "HD")
//Navigate to Downloads Library Provider
.NavigateToFolder("Queue Row Item Collection")
.InvokeLibraryAddDialog();
//Get parts to add
string rowItemPath = MatterControlUtilities.GetTestItemPath("Batman.stl");
testRunner.Delay()
.Type(MatterControlUtilities.GetTestItemPath("Batman.stl"))
.Delay()
.Type("{Enter}")
//Get test results
.ClickByName("Row Item Batman.stl")
.ClickByName("Print Library Overflow Menu")
.ClickByName("Export Menu Item")
.WaitForName("Export Item Window");
string gcodeOutputPath = MatterControlUtilities.PathToExportGcodeFolder;
Directory.CreateDirectory(gcodeOutputPath);
string fullPathToGcodeFile = Path.Combine(gcodeOutputPath, "Batman");
testRunner.ClickByName("Machine File (G-Code) Button")
.ClickByName("Export Button")
.Delay()
.Type(fullPathToGcodeFile)
.Type("{Enter}")
.Assert(() => File.Exists(fullPathToGcodeFile + ".gcode"), "Exported file not found");
// add an item to the bed, and export it to gcode
fullPathToGcodeFile = Path.Combine(gcodeOutputPath, "Cube");
testRunner.AddItemToBed()
.ClickByName("PrintPopupMenu")
.ClickByName("Export GCode Button")
.Type(fullPathToGcodeFile)
.Type("{Enter}")
.Assert(() => File.Exists(fullPathToGcodeFile + ".gcode"), "Exported file not found");
return Task.FromResult(0);
});
}
[Test, ChildProcessTest]
public async Task ExportDesignTabAsSTL()
{
await MatterControlUtilities.RunTest(testRunner =>
{
testRunner.WaitForFirstDraw();
// save from design tab
var gcodeOutputPath = MatterControlUtilities.PathToExportGcodeFolder;
var fullPathToGcodeFile = Path.Combine(gcodeOutputPath, "Cube2");
Directory.CreateDirectory(gcodeOutputPath);
testRunner.EnsureWelcomePageClosed()
.ClickByName("Create New")
.AddItemToBed()
.ClickByName("Bed Options Menu")
.ClickByName("Export Menu Item")
.WaitForName("Export Item Window");
testRunner.ClickByName("STL File Button")
.ClickByName("Export Button")
.Delay()
.Type(fullPathToGcodeFile)
.Type("{Enter}")
.WaitFor(() => File.Exists(fullPathToGcodeFile + ".stl"), 10);
testRunner.WaitFor(() => File.Exists(fullPathToGcodeFile + ".stl"), 10);
Assert.IsTrue(File.Exists(fullPathToGcodeFile + ".stl"), "Exported file not found");
return Task.FromResult(0);
});
}
[Test, ChildProcessTest]
public async Task ExportStreamG92HandlingTest()
{
await MatterControlUtilities.RunTest(testRunner =>
{
testRunner.WaitForFirstDraw();
testRunner.CloneAndSelectPrinter("No Retraction after Purge");
var printer = testRunner.FirstPrinter();
//Navigate to Downloads Library Provider
testRunner.NavigateToFolder("Queue Row Item Collection");
testRunner.InvokeLibraryAddDialog();
//Get parts to add
string rowItemPath = MatterControlUtilities.GetTestItemPath("Batman.stl");
testRunner.Delay()
.Type(MatterControlUtilities.GetTestItemPath("Batman.stl"))
.Delay()
.Type("{Enter}");
//Get test results
testRunner.ClickByName("Row Item Batman.stl")
.ClickByName("Print Library Overflow Menu")
.ClickByName("Export Menu Item")
.WaitForName("Export Item Window");
testRunner.ClickByName("Machine File (G-Code) Button")
.ClickByName("Export Button");
string gcodeOutputPath = MatterControlUtilities.PathToExportGcodeFolder;
Directory.CreateDirectory(gcodeOutputPath);
string fullPathToGcodeFile = Path.Combine(gcodeOutputPath, "Batman");
testRunner.Type(fullPathToGcodeFile);
testRunner.Type("{Enter}");
var filename = fullPathToGcodeFile + ".gcode";
testRunner.WaitFor(() => File.Exists(filename), 10)
.WaitFor(() => !IsFileLocked(filename), 1000)
.Delay(2);
// validate that the actual printer output has the right lines
var expectedLines = new string[]
{
"G28 ; home all axes",
"M280 P0 S160",
"G4 P400",
"M280 P0 S90",
"M109 S205",
"G1 X5 Y5 Z3.13 F1800",
"G92 E0 ; Purge line",
"G1 X5.83 Z3.04 E0.833 F900",
"G1 X6.67 Z2.96 E1.667",
"G1 X7.5 Z2.87 E2.5",
"G1 X8.33 Z2.79 E3.333",
"G1 X9.17 Z2.7 E4.167",
"G1 X10 Z2.62 E5",
"G92 E0 ; Purge line",
"G1 E-2 F2400",
"M75 ; start print timer",
};
var actualLines = File.ReadAllLines(filename);
ValidateLinesStartingWithFirstExpected(expectedLines, actualLines);
// make sure the file has the expected header
return Task.FromResult(0);
}, maxTimeToRun: 200);
}
private bool IsFileLocked(string file)
{
try
{
using (FileStream stream = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.None))
{
stream.Close();
}
}
catch (IOException)
{
//the file is unavailable because it is:
//still being written to
//or being processed by another thread
//or does not exist (has already been processed)
return true;
}
//file is not locked
return false;
}
private void ValidateLinesStartingWithFirstExpected(string[] expectedLines, string[] actualLines)
{
// search actual lines until we find the first expectedLine
for (int i = 0; i < actualLines.Length; i++)
{
if (actualLines[i] == expectedLines[0])
{
for (int j = 0; j < expectedLines.Length; j++)
{
Assert.AreEqual(expectedLines[j], actualLines[i + j], "All lines should match");
// Debug.WriteLine("\"" + actualLines[i + j] + "\",");
}
return;
}
}
throw new Exception("Did not find the first expected line");
}
}
}

View file

@ -0,0 +1,91 @@
using System.Threading;
using System.Threading.Tasks;
using NUnit.Framework;
using TestInvoker;
namespace MatterHackers.MatterControl.Tests.Automation
{
[TestFixture, Category("MatterControl.UI.Automation"), Parallelizable(ParallelScope.Children)]
public class HardwareLevelingUITests
{
[Test, ChildProcessTest]
public async Task HasHardwareLevelingHidesLevelingSettings()
{
await MatterControlUtilities.RunTest((testRunner) =>
{
testRunner.WaitForFirstDraw();
// Add printer that has hardware leveling
testRunner.AddAndSelectPrinter("Airwolf 3D", "HD");
testRunner.SwitchToPrinterSettings();
testRunner.ClickByName("Features SliceSettingsTab");
testRunner.ClickByName("Slice Settings Overflow Menu");
testRunner.ClickByName("Advanced Menu Item");
Assert.IsFalse(testRunner.WaitForName("print_leveling_solution Row", .5), "Print leveling should not exist for an Airwolf HD");
// Add printer that does not have hardware leveling
testRunner.AddAndSelectPrinter("3D Factory", "MendelMax 1.5");
testRunner.SwitchToPrinterSettings();
testRunner.ClickByName("Features SliceSettingsTab");
testRunner.ClickByName("Slice Settings Overflow Menu");
testRunner.ClickByName("Advanced Menu Item");
Assert.IsTrue(testRunner.WaitForName("print_leveling_solution Row"), "Print leveling should exist for a 3D Factory MendelMax");
return Task.CompletedTask;
}, overrideHeight: 800);
}
// NOTE: This test once failed, due to timing probably.
[Test, ChildProcessTest, Category("Emulator")]
public async Task SoftwareLevelingTest()
{
await MatterControlUtilities.RunTest((testRunner) =>
{
// make a jump start printer
using (var emulator = testRunner.LaunchAndConnectToPrinterEmulator("JumpStart", "V1", runSlow: false))
{
// make sure it is showing the correct button
testRunner.OpenPrintPopupMenu();
var startPrintButton = testRunner.GetWidgetByName("Start Print Button", out _);
Assert.IsFalse(startPrintButton.Enabled, "Start Print should not be enabled");
testRunner.ClickByName("SetupPrinter");
testRunner.Complete9StepLeveling();
// Satisfy non-empty bed requirement
testRunner.AddItemToBed();
testRunner.OpenPrintPopupMenu();
// make sure the button has changed to start print
startPrintButton = testRunner.GetWidgetByName("Start Print Button", out _);
Assert.IsTrue(startPrintButton.Enabled, "Start Print should be enabled after running printer setup");
Assert.IsFalse(testRunner.WaitForName("SetupPrinter", .5), "Finish Setup should not be visible after leveling the printer");
// reset to defaults and make sure print leveling is cleared
testRunner.SwitchToSliceSettings();
testRunner.WaitForReloadAll(() =>
{
testRunner.ClickByName("Printer Overflow Menu");
testRunner.ClickByName("Reset to Defaults... Menu Item");
testRunner.ClickByName("Yes Button");
});
testRunner.OpenPrintPopupMenu();
// make sure it is showing the correct button
Assert.IsTrue(testRunner.WaitForName("SetupPrinter"), "Setup... should be visible after reset to Defaults");
}
return Task.CompletedTask;
}, maxTimeToRun: 90); // NOTE: This test got stuck in ClickByName("Yes Button") -> WaitforDraw. It appears to be because WaitforDraw waited for a closed window to redraw itself.
}
}
}

View file

@ -0,0 +1,192 @@
/*
Copyright (c) 2017, Lars Brubaker, John Lewin
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the FreeBSD Project.
*/
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using MatterHackers.Agg.UI;
using NUnit.Framework;
using TestInvoker;
namespace MatterHackers.MatterControl.Tests.Automation
{
[TestFixture, Ignore("Product code still needs to be implemented"), Category("MatterControl.UI.Automation")]
public class LibraryActionTests
{
[Test, ChildProcessTest]
public async Task ClickOnExportButton()
{
await MatterControlUtilities.RunTest(testRunner =>
{
// Tests that clicking the queue export button with a single item selected opens export item window
testRunner.AddAndSelectPrinter();
//Make sure that the export window does not exist
bool exportWindowExists1 = testRunner.WaitForName("Export Item Window", 0);
Assert.IsTrue(exportWindowExists1 == false, "Export window does not exist");
testRunner.NavigateToFolder("Queue Row Item Collection");
testRunner.ClickByName("Queue Export Button");
SystemWindow containingWindow;
GuiWidget exportWindow = testRunner.GetWidgetByName("Export Item Window", out containingWindow, 5);
Assert.IsTrue(exportWindow != null, "Export window does exist");
return Task.CompletedTask;
}, queueItemFolderToAdd: QueueTemplate.Three_Queue_Items);
}
/// <summary>
/// Confirms the Export to Zip feature compresses and exports to a zip file and that file imports without issue
/// </summary>
/// <returns></returns>
[Test, ChildProcessTest]
public async Task ExportToZipImportFromZip()
{
await MatterControlUtilities.RunTest(testRunner =>
{
// Ensure output file does not exist
string exportZipPath = MatterControlUtilities.GetTestItemPath("TestExportZip.zip");
if (File.Exists(exportZipPath))
{
File.Delete(exportZipPath);
}
testRunner.AddAndSelectPrinter();
Assert.AreEqual(4, QueueData.Instance.ItemCount, "Queue should initially have 4 items");
// Invoke Queue -> Export to Zip dialog
testRunner.ClickByName("Queue... Menu");
testRunner.Delay(.2);
testRunner.ClickByName(" Export to Zip Menu Item");
testRunner.Delay(2);
testRunner.Type(exportZipPath);
testRunner.Delay(2);
testRunner.Type("{Enter}");
testRunner.WaitFor(() => File.Exists(exportZipPath));
Assert.IsTrue(File.Exists(exportZipPath), "Queue was exported to zip file, file exists on disk at expected path");
// Import the exported zip file and confirm the Queue Count increases by 3
testRunner.InvokeLibraryAddDialog();
testRunner.Delay(1);
testRunner.Type(exportZipPath);
testRunner.Delay(1);
testRunner.Type("{Enter}");
testRunner.WaitFor(() => QueueData.Instance.ItemCount == 8);
Assert.AreEqual(8, QueueData.Instance.ItemCount, "All parts imported successfully from exported zip");
testRunner.Delay(.3);
try
{
if (File.Exists(exportZipPath))
{
File.Delete(exportZipPath);
}
}
catch { }
return Task.CompletedTask;
}, queueItemFolderToAdd: QueueTemplate.Three_Queue_Items);
}
[Test, ChildProcessTest, Ignore("Test now works as expected but product does not implement expected functionality")]
public async Task QueueExportIsDisabledIfEmpty()
{
await MatterControlUtilities.RunTest(testRunner =>
{
testRunner.AddAndSelectPrinter();
testRunner.ClickByName("Queue... Menu");
var exportButton = testRunner.GetWidgetByName(" Export to Zip Menu Item", out _, 5);
Assert.IsNotNull(exportButton, "Export button should exist");
Assert.IsTrue(exportButton.Enabled, "Export button should be enabled");
testRunner.ClickByName(" Remove All Menu Item");
testRunner.Delay(1);
testRunner.ClickByName("Queue... Menu");
testRunner.WaitFor(() => !exportButton.Enabled, 4);
Assert.IsFalse(exportButton.Enabled, "Export button should be disabled after Queue Menu -> Remove All");
return Task.CompletedTask;
});
}
[Test, ChildProcessTest, Ignore("Not Finished")]
public async Task ClickCreatePartSheetButton()
{
await MatterControlUtilities.RunTest(testRunner =>
{
testRunner.AddAndSelectPrinter();
testRunner.ChangeToQueueContainer();
bool queueEmpty = true;
int queueItemCount = QueueData.Instance.ItemCount;
if (queueItemCount == 3)
{
queueEmpty = false;
}
Assert.IsTrue(queueEmpty == false);
testRunner.ClickByName("Queue... Menu");
testRunner.Delay(.2);
testRunner.ClickByName(" Create Part Sheet Menu Item");
testRunner.Delay(2);
string pathToSavePartSheet = MatterControlUtilities.GetTestItemPath("CreatePartSheet");
string validatePartSheetPath = Path.Combine("..", "..", "..", "TestData", "QueueItems", "CreatePartSheet.pdf");
testRunner.Type(pathToSavePartSheet);
testRunner.Delay(1);
testRunner.Type("{Enter}");
testRunner.Delay(1);
testRunner.Delay(5);
bool partSheetCreated = File.Exists(validatePartSheetPath);
testRunner.Delay(2);
Assert.IsTrue(partSheetCreated == true);
if (File.Exists(validatePartSheetPath))
{
File.Delete(validatePartSheetPath);
}
return Task.CompletedTask;
}, queueItemFolderToAdd: QueueTemplate.Three_Queue_Items);
}
}
}

View file

@ -0,0 +1,226 @@
/*
Copyright (c) 2017, John Lewin
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the FreeBSD Project.
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using MatterHackers.Agg;
using MatterHackers.Agg.Platform;
using MatterHackers.Agg.UI;
using MatterHackers.MatterControl.DataStorage;
using MatterHackers.MatterControl.Library;
using MatterHackers.MatterControl.Tests.Automation;
using NUnit.Framework;
using TestInvoker;
namespace MatterControl.Tests.MatterControl
{
[TestFixture, Category("LibraryContainerTests")]
public class LibraryContainerTests
{
[SetUp]
public static void Setup()
{
StaticData.RootPath = MatterControlUtilities.StaticDataPath;
MatterControlUtilities.OverrideAppDataLocation(MatterControlUtilities.RootPath);
}
[Test]
public Task TestExistsForEachContainerType()
{
// Find all test methods on this test class
var thisClassMethods = this.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance);
// Find and validate all ILibraryContainer types, skipping abstract classes
foreach (var containerType in PluginFinder.FindTypes<ILibraryContainer>().Where(fieldType => !fieldType.IsAbstract))
{
string expectedTestName = $"{containerType.Name}Test";
Assert.AreEqual(
1,
thisClassMethods.Where(m => m.Name == expectedTestName).Count(),
"Test for LibraryContainer missing, not yet created or typo'd - Expected: " + expectedTestName);
}
return Task.CompletedTask;
}
[Test]
public async Task NoContentChangedOnLoad()
{
bool onIdlePumpActive = true;
var uiPump = Task.Run(() =>
{
while (onIdlePumpActive)
{
UiThread.InvokePendingActions();
Thread.Sleep(10);
};
Console.Write("Exiting");
});
// Find and validate all ILibraryContainer types, skipping abstract classes
foreach (var containerType in PluginFinder.FindTypes<ILibraryContainer>().Where(fieldType => !fieldType.IsAbstract))
{
var args = new List<object>();
if (containerType == typeof(FileSystemContainer))
{
args.Add(MatterControlUtilities.RootPath);
}
else if (containerType == typeof(RootLibraryContainer))
{
// TODO: Not sure how to test RootLibrary given content loads after MatterControl init is finished, skipping for now
continue;
}
if (Activator.CreateInstance(containerType, args.ToArray()) is ILibraryContainer libraryContainer)
{
if (libraryContainer is ZipMemoryContainer zipContainer)
{
zipContainer.Path = Path.Combine(MatterControlUtilities.RootPath, "Tests", "TestData", "TestParts", "Batman.zip");
zipContainer.RelativeDirectory = Path.GetDirectoryName(zipContainer.Path);
}
int changedCount = 0;
libraryContainer.ContentChanged += (s, e) =>
{
changedCount++;
};
await Task.Run(() =>
{
libraryContainer.Load();
});
// Allow time for invalid additional reloads
await Task.Delay(300);
// Verify Reload is called;
Assert.AreEqual(0, changedCount, "Expected reload count not hit - container should fire reload event after acquiring content");
}
}
onIdlePumpActive = false;
}
[Test]
public async Task AddFiresContentChangedEvent()
{
string filePath = Path.Combine(MatterControlUtilities.RootPath, "Tests", "TestData", "TestParts", "Batman.stl");
bool onIdlePumpActive = true;
var uiPump = Task.Run(() =>
{
while (onIdlePumpActive)
{
UiThread.InvokePendingActions();
Thread.Sleep(10);
};
Console.Write("Exiting");
});
Type writable = typeof(ILibraryWritableContainer);
// Find and validate all ILibraryContainer types, skipping abstract classes
foreach (var containerType in PluginFinder.FindTypes<ILibraryContainer>().Where(fieldType => !fieldType.IsAbstract))
{
var args = new List<object>();
if (containerType == typeof(FileSystemContainer))
{
Directory.CreateDirectory(ApplicationDataStorage.Instance.ApplicationTempDataPath);
args.Add(ApplicationDataStorage.Instance.ApplicationTempDataPath);
}
else if (containerType == typeof(RootLibraryContainer)
|| !writable.IsAssignableFrom(containerType))
{
// TODO: Not sure how to test RootLibrary given content loads after MatterControl init is finished, skipping for now
continue;
}
if (Activator.CreateInstance(containerType, args.ToArray()) is ILibraryWritableContainer libraryContainer)
{
if (!libraryContainer.AllowAction(ContainerActions.AddItems))
{
continue;
}
if (libraryContainer is ZipMemoryContainer zipContainer)
{
zipContainer.Path = Path.Combine(MatterControlUtilities.RootPath, "Tests", "TestData", "TestParts", "Batman.zip");
zipContainer.RelativeDirectory = Path.GetDirectoryName(zipContainer.Path);
}
int changedCount = 0;
libraryContainer.ContentChanged += (s, e) =>
{
changedCount++;
};
var waitUntil = DateTime.Now.AddSeconds(15);
var result = Task.Run(() =>
{
libraryContainer.Load();
libraryContainer.Add(new[] { new FileSystemFileItem(filePath) });
});
// Wait for reload
while (DateTime.Now <= waitUntil)
{
if (changedCount > 0)
{
break;
}
await Task.Delay(200);
}
// Allow time for invalid additional reloads
await Task.Delay(300);
Console.WriteLine($"ContentChanged for {containerType.Name}");
// Verify Reload is called;
Assert.AreEqual(1, changedCount, $"Expected reload count for {containerType.Name} not hit - container should fire reload event after acquiring content");
}
}
onIdlePumpActive = false;
}
}
}

View file

@ -0,0 +1,147 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using MatterHackers.Agg.UI.Tests;
using MatterHackers.GuiAutomation;
using NUnit.Framework;
using TestInvoker;
namespace MatterHackers.MatterControl.Tests.Automation
{
[TestFixture, Category("MatterControl.UI.Automation")]
public class LibraryDownloadsTests
{
[SetUp]
public void Setup()
{
MatterControlUtilities.CreateDownloadsSubFolder();
}
[TearDown]
public void TearDown()
{
MatterControlUtilities.DeleteDownloadsSubFolder();
}
[Test, ChildProcessTest]
public async Task DownloadsAddButtonAddsMultipleFiles()
{
await MatterControlUtilities.RunTest(testRunner =>
{
testRunner.AddAndSelectPrinter();
// Navigate to Downloads Library Provider
testRunner.NavigateToFolder("Downloads Row Item Collection");
testRunner.NavigateToFolder("-Temporary Row Item Collection");
// Add both files to the FileOpen dialog
testRunner.InvokeLibraryAddDialog();
testRunner.CompleteDialog(
string.Format(
"\"{0}\";\"{1}\"",
MatterControlUtilities.GetTestItemPath("Fennec_Fox.stl"),
MatterControlUtilities.GetTestItemPath("Batman.stl")),
5);
Assert.IsTrue(testRunner.WaitForName("Row Item Fennec_Fox.stl", 2), "Fennec Fox item exists");
Assert.IsTrue(testRunner.WaitForName("Row Item Batman.stl", 2), "Batman item exists");
return Task.CompletedTask;
});
}
[Test, ChildProcessTest]
public async Task DownloadsAddButtonAddsAMFFiles()
{
await MatterControlUtilities.RunTest(testRunner =>
{
testRunner.AddAndSelectPrinter();
// Navigate to Downloads Library Provider
testRunner.NavigateToFolder("Downloads Row Item Collection");
testRunner.NavigateToFolder("-Temporary Row Item Collection");
// Add AMF part items to Downloads and then type paths into file dialog
testRunner.InvokeLibraryAddDialog();
testRunner.CompleteDialog(MatterControlUtilities.GetTestItemPath("Rook.amf"), 4);
Assert.IsTrue(testRunner.WaitForName("Row Item Rook.amf"), "Rook item exists");
return Task.CompletedTask;
});
}
[Test, ChildProcessTest]
public async Task DownloadsAddButtonAddsZipFiles()
{
await MatterControlUtilities.RunTest(testRunner =>
{
testRunner.AddAndSelectPrinter();
// Navigate to Downloads Library Provider
testRunner.NavigateToFolder("Downloads Row Item Collection");
testRunner.NavigateToFolder("-Temporary Row Item Collection");
testRunner.InvokeLibraryAddDialog();
testRunner.CompleteDialog(MatterControlUtilities.GetTestItemPath("Test.zip"), 4);
testRunner.DoubleClickByName("Test.zip Row Item Collection");
testRunner.DoubleClickByName("TestCompress.zip Row Item Collection");
Assert.IsTrue(testRunner.WaitForName("Row Item Chinese Dragon.stl", 2), "Chinese Dragon item exists");
Assert.IsTrue(testRunner.WaitForName("Row Item chichen-itza_pyramid.stl", 2), "chichen-itza item exists");
Assert.IsTrue(testRunner.WaitForName("Row Item Circle Calibration.stl", 2), "Circle Calibration item exists");
return Task.CompletedTask;
});
}
[Test, ChildProcessTest]
public async Task RenameDownloadsPrintItem()
{
await MatterControlUtilities.RunTest(testRunner =>
{
testRunner.AddAndSelectPrinter();
// Navigate to Downloads Library Provider
testRunner.NavigateToFolder("Downloads Row Item Collection");
testRunner.NavigateToFolder("-Temporary Row Item Collection");
testRunner.InvokeLibraryAddDialog();
testRunner.CompleteDialog(MatterControlUtilities.GetTestItemPath("Batman.stl"), 2);
// Rename added item
testRunner.ClickByName("Row Item Batman.stl");
testRunner.LibraryRenameSelectedItem();
testRunner.WaitForName("InputBoxPage Action Button");
testRunner.Type("Batman Renamed");
testRunner.ClickByName("InputBoxPage Action Button");
Assert.IsTrue(testRunner.WaitForName("Row Item Batman Renamed.stl", 2));
return Task.CompletedTask;
});
}
[Test, ChildProcessTest]
public async Task CreateFolder()
{
await MatterControlUtilities.RunTest(testRunner =>
{
testRunner.AddAndSelectPrinter();
//Navigate to Downloads Library Provider
testRunner.NavigateToFolder("Downloads Row Item Collection");
testRunner.NavigateToFolder("-Temporary Row Item Collection");
testRunner.CreateChildFolder("New Folder");
return Task.CompletedTask;
});
}
}
}

View file

@ -0,0 +1,260 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MatterHackers.Agg.UI;
using MatterHackers.MatterControl.PrintQueue;
using NUnit.Framework;
using TestInvoker;
namespace MatterHackers.MatterControl.Tests.Automation
{
// Most of these tests are disabled. Local Library needs to be added by InitializeLibrary() (MatterHackers.MatterControl.ApplicationController).
[TestFixture, Category("MatterControl.UI.Automation"), Parallelizable(ParallelScope.Children)]
public class LocalLibraryTests
{
[Test, ChildProcessTest]
public async Task LocalLibraryAddButtonAddSingleItemToLibrary()
{
await MatterControlUtilities.RunTest((testRunner) =>
{
testRunner.AddAndSelectPrinter();
testRunner.AddTestAssetsToLibrary(new[] { "Batman.stl" });
return Task.CompletedTask;
});
}
[Test, ChildProcessTest]
public async Task LocalLibraryAddButtonAddsMultipleItemsToLibrary()
{
await MatterControlUtilities.RunTest((testRunner) =>
{
testRunner.AddAndSelectPrinter();
testRunner.AddTestAssetsToLibrary(new[] { "Rook.amf", "Batman.stl" });
return Task.CompletedTask;
});
}
[Test, ChildProcessTest]
public async Task LocalLibraryAddButtonAddAMFToLibrary()
{
await MatterControlUtilities.RunTest((testRunner) =>
{
testRunner.AddAndSelectPrinter();
testRunner.AddTestAssetsToLibrary(new[] { "Rook.amf" });
return Task.CompletedTask;
}, overrideWidth: 1024, overrideHeight: 800);
}
[Test, ChildProcessTest]
public async Task ParentFolderRefreshedOnPathPop()
{
// Expected: When descending into a child folder and moving items into the parent, popping the path to the parent should refresh and show the moved content
await MatterControlUtilities.RunTest(testRunner =>
{
testRunner.AddAndSelectPrinter();
// Navigate to Local Library
testRunner.NavigateToFolder("Local Library Row Item Collection");
string folderID = testRunner.CreateChildFolder("New Folder");
testRunner.DoubleClickByName(folderID);
// Add Library item
testRunner.InvokeLibraryAddDialog();
testRunner.Delay(2);
testRunner.Type(MatterControlUtilities.GetTestItemPath("Batman.stl"));
testRunner.Delay(1);
testRunner.Type("{Enter}");
string newFileID = "Row Item Batman";
testRunner.ClickByName(newFileID);
testRunner.LibraryMoveSelectedItem();
testRunner.NavigateToFolder("Local Library Row Item Collection");
// Click Move
testRunner.ClickByName("Accept Button");
// Wait for closed window/closed row
testRunner.WaitForWidgetDisappear("Move Item Window", 5);
testRunner.WaitForWidgetDisappear("Row Item Batman", 2);
// Return to the Local Library folder
testRunner.ClickByName("Library Up Button");
// Assert that the expected item appears in the parent after popping the path
testRunner.ClickByName(newFileID);
return Task.CompletedTask;
});
}
[Test, ChildProcessTest]
public async Task LocalLibraryAddButtonAddZipToLibrary()
{
await MatterControlUtilities.RunTest((testRunner) =>
{
testRunner.AddAndSelectPrinter();
// Navigate to Local Library
testRunner.NavigateToFolder("Local Library Row Item Collection");
// Make sure that Item does not exist before the test begins
Assert.IsFalse(testRunner.WaitForName("Row Item Batman", 1), "Batman part should not exist at test start");
Assert.IsFalse(testRunner.WaitForName("Row Item 2013-01-25 Mouthpiece V2", 1), "Mouthpiece part should not exist at test start");
// Add Library item
testRunner.InvokeLibraryAddDialog();
testRunner.Delay(2);
testRunner.Type(MatterControlUtilities.GetTestItemPath("Batman.zip"));
testRunner.Delay(1);
testRunner.Type("{Enter}");
testRunner.WaitForName("Batman.zip Row Item Collection");
testRunner.DoubleClickByName("Batman.zip Row Item Collection");
Assert.IsTrue(testRunner.WaitForName("Row Item Batman.stl"), "Batman part should exist after adding");
Assert.IsTrue(testRunner.WaitForName("Row Item 2013-01-25_Mouthpiece_v2.stl"), "Mouthpiece part should exist after adding");
return Task.CompletedTask;
});
}
[Test, ChildProcessTest]
public async Task DoubleClickSwitchesToOpenTab()
{
await MatterControlUtilities.RunTest((testRunner) =>
{
MatterControlUtilities.CreateDownloadsSubFolder();
testRunner.AddAndSelectPrinter();
testRunner.AddItemToBed()
.ClickByName("Save Menu SplitButton", offset: new Agg.Point2D(30, 0))
.ClickByName("Save As Menu Item")
.NavigateToFolder("Downloads Row Item Collection")
.NavigateToFolder("-Temporary Row Item Collection")
.ClickByName("Design Name Edit Field")
.Type("Cube Design")
.ClickByName("Accept Button");
var mainViewWidget = ApplicationController.Instance.MainView;
var tabControl = mainViewWidget.TabControl;
Assert.AreEqual(5, mainViewWidget.TabControl.AllTabs.Count());
// open the design for editing
testRunner.ClickByName("Library Tab")
.NavigateToFolder("Downloads Row Item Collection")
.NavigateToFolder("-Temporary Row Item Collection")
.DoubleClickByName("Row Item Cube Design.mcx")
.WaitFor(() => mainViewWidget.TabControl.AllTabs.Count() == 6);
// we have opened a new tab
Assert.AreEqual(6, mainViewWidget.TabControl.AllTabs.Count());
// we are on the design tab
Assert.AreEqual(5, tabControl.SelectedTabIndex);
Assert.AreEqual("New Design", tabControl.SelectedTabKey);
// double click it again and prove that it goes to the currently open tab
testRunner.ClickByName("Library Tab")
.DoubleClickByName("Row Item Cube Design.mcx");
// we have not opened a new tab
Assert.AreEqual(6, mainViewWidget.TabControl.AllTabs.Count());
// we are on the design tab
Assert.AreEqual(5, tabControl.SelectedTabIndex);
// rename in the library tab
// assert tab name has change
// rename from the tab
// assert name in library tab has changed
MatterControlUtilities.DeleteDownloadsSubFolder();
return Task.CompletedTask;
});
}
[Test, ChildProcessTest]
public async Task RenameButtonRenamesLocalLibraryFolder()
{
await MatterControlUtilities.RunTest((testRunner) =>
{
testRunner.AddAndSelectPrinter();
// Navigate to Local Library
testRunner.NavigateToFolder("Local Library Row Item Collection");
// Create New Folder
string folderID = testRunner.CreateChildFolder("New Folder");
testRunner.ClickByName(folderID);
testRunner.Delay(.2);
testRunner.LibraryRenameSelectedItem();
testRunner.Delay(.5);
testRunner.Type("Renamed Library Folder");
testRunner.ClickByName("InputBoxPage Action Button");
testRunner.Delay(.2);
// Make sure the renamed Library Folder exists
Assert.IsTrue(testRunner.WaitForName("Renamed Library Folder Row Item Collection"), "Renamed folder should exist");
return Task.CompletedTask;
});
}
[Test, ChildProcessTest]
public async Task RemoveButtonClickedRemovesSingleItem()
{
await MatterControlUtilities.RunTest((testRunner) =>
{
testRunner.AddAndSelectPrinter();
testRunner.AddTestAssetsToLibrary(new[] { "Rook.amf" });
// Select and remove item
testRunner.ClickByName("Row Item Rook");
testRunner.LibraryRemoveSelectedItem();
// Make sure that the item has been removed
Assert.IsFalse(testRunner.WaitForName("Row Item Rook", .5));
return Task.CompletedTask;
});
}
[Test, ChildProcessTest]
public async Task RemoveButtonClickedRemovesMultipleItems()
{
await MatterControlUtilities.RunTest((testRunner) =>
{
testRunner.AddAndSelectPrinter();
testRunner.AddTestAssetsToLibrary(new[] { "Rook.amf", "Batman.stl" });
// Select both items
testRunner.SelectListItems("Row Item Rook", "Row Item Batman");
// Remove items
testRunner.LibraryRemoveSelectedItem();
testRunner.Delay(1);
// Make sure both selected items are removed
Assert.IsFalse(testRunner.WaitForName("Row Item Rook", 1), "Rook part should *not* exist after remove");
Assert.IsFalse(testRunner.WaitForName("Row Item Batman", 1), "Batman part *not* exist after remove");
return Task.CompletedTask;
});
}
}
}

View file

@ -0,0 +1,55 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0-windows</TargetFramework>
<OutputType>Library</OutputType>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
<RuntimeIdentifier>win</RuntimeIdentifier>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<AssemblyName>$(MSBuildProjectName)</AssemblyName>
<RootNamespace>$(MSBuildProjectName.Replace(" ", "_"))</RootNamespace>
<BaseIntermediateOutputPath>obj\</BaseIntermediateOutputPath>
<PackageId>$(AssemblyName)</PackageId>
<Version>$(VersionPrefix)$(OutputPath)</Version>
<Authors>$(AssemblyName)</Authors>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.4.2" />
<PackageReference Include="Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers" Version="0.4.410601">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<Compile Include="..\MatterControl.Tests\MatterControl\MatterControlUtilities.cs">
<Link>MatterControlUtilities.cs</Link>
</Compile>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\MatterControl.Common\MatterControl.Common.csproj" />
<ProjectReference Include="..\..\MatterControl.csproj" />
<ProjectReference Include="..\..\MatterControl.MeshOperations\MatterControl.MeshOperations.csproj" />
<ProjectReference Include="..\..\MatterControl.Printing\MatterControl.Printing.csproj" />
<ProjectReference Include="..\..\MatterControl.Winforms\MatterControl.Winforms.csproj" />
<ProjectReference Include="..\..\MatterControlLib\MatterControlLib.csproj" />
<ProjectReference Include="..\..\Submodules\agg-sharp\agg\Agg.csproj" />
<ProjectReference Include="..\..\Submodules\agg-sharp\DataConverters3D\DataConverters3D.csproj" />
<ProjectReference Include="..\..\Submodules\agg-sharp\GuiAutomation\GuiAutomation.csproj" />
<ProjectReference Include="..\..\Submodules\agg-sharp\Gui\Gui.csproj" />
<ProjectReference Include="..\..\Submodules\agg-sharp\PlatformWin32\PlatformWin32.csproj" />
<ProjectReference Include="..\..\Submodules\agg-sharp\PolygonMesh\PolygonMesh.csproj" />
<ProjectReference Include="..\..\Submodules\agg-sharp\RenderOpenGl\RenderOpenGl.csproj" />
<ProjectReference Include="..\..\Submodules\agg-sharp\Tests\Agg.Tests\Agg.Tests.csproj" />
<ProjectReference Include="..\..\Submodules\agg-sharp\VectorMath\VectorMath.csproj" />
<ProjectReference Include="..\..\Submodules\MatterSlice\MatterSliceLib\MatterSliceLib.csproj" />
<ProjectReference Include="..\..\Submodules\MatterSlice\Tests\MatterSlice.Tests\MatterSlice.Tests.csproj" />
</ItemGroup>
<ItemGroup>
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
</ItemGroup>
<ItemGroup>
<Compile Remove="SlicingTests.cs" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,121 @@
/*
Copyright (c) 2017, John Lewin
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the FreeBSD Project.
*/
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MatterHackers.MatterControl.Library;
using MatterHackers.MatterControl.PartPreviewWindow;
using MatterHackers.MatterControl.SlicerConfiguration;
using NUnit.Framework;
using TestInvoker;
namespace MatterHackers.MatterControl.Tests.Automation
{
[TestFixture, Category("MatterControl.UI.Automation"), Parallelizable(ParallelScope.Children)]
public class MatterControlTests
{
[Test, ChildProcessTest]
public async Task ThumbnailGenerationMode()
{
await MatterControlUtilities.RunTest(async (testRunner) =>
{
// Automation tests should initialize with orthographic thumbnails
var item = new FileSystemFileItem(MatterControlUtilities.GetTestItemPath("Rook.amf"));
var provider = ApplicationController.Instance.Library.GetContentProvider(item);
// Generate thumbnail
var stopWatch = Stopwatch.StartNew();
await provider.GetThumbnail(item, 400, 400);
Assert.Less(stopWatch.ElapsedMilliseconds, 2000, "Elapsed thumbnail generation for Rook.amf should be less than 2 seconds for expected orthographic mode");
});
}
[Test, ChildProcessTest]
public async Task ViewGenerateSupportMenu()
{
await MatterControlUtilities.RunTest(testRunner =>
{
testRunner.WaitForFirstDraw();
testRunner.AddAndSelectPrinter("Airwolf 3D", "HD");
testRunner.ClickByName("Support SplitButton");
testRunner.ClickByName("Generate Support Button");
return Task.CompletedTask;
});
}
[Test, ChildProcessTest]
public async Task PrinterTabRemainsAfterReloadAll()
{
await MatterControlUtilities.RunTest((testRunner) =>
{
testRunner.WaitForFirstDraw();
// Add Guest printers
testRunner.AddAndSelectPrinter("Airwolf 3D", "HD");
Assert.AreEqual(
1,
ApplicationController.Instance.ActivePrinters.Count(),
"One printer should exist after Airwolf add");
testRunner.SwitchToSliceSettings();
// Move to Adhesion tab
testRunner.SelectSliceSettingsField(SettingsKey.skirts);
// Click Brim toggle field forcing ReloadAll
testRunner.WaitForReloadAll(() =>
{
testRunner.ClickByName("Create Brim Field");
ApplicationController.Instance.ReloadAll();
});
// Ensure tabs remain
Assert.AreEqual(
1,
ApplicationController.Instance.ActivePrinters.Count(),
"One printer should exist after Airwolf add");
testRunner.Delay(1);
Assert.AreEqual(
1,
ApplicationController.Instance.MainView.TabControl.AllTabs.Select(t => t.TabContent).OfType<PrinterTabPage>().Count(),
"One printer tab should exist after ReloadAll");
return Task.CompletedTask;
}, maxTimeToRun: 120);
}
}
}

View file

@ -0,0 +1,32 @@
using System.Threading;
using System.Threading.Tasks;
using MatterHackers.Agg.UI;
using NUnit.Framework;
using TestInvoker;
namespace MatterHackers.MatterControl.Tests.Automation
{
[TestFixture, Category("MatterControl.UI.Automation")]
public class ShowTerminalButtonClickedOpensTerminal
{
[Test, ChildProcessTest]
public async Task ClickingShowTerminalButtonOpensTerminal()
{
await MatterControlUtilities.RunTest((testRunner) =>
{
testRunner.AddAndSelectPrinter("Airwolf 3D", "HD");
Assert.IsFalse(testRunner.WaitForName("TerminalWidget", 0.5), "Terminal Window should not exist");
// when we start up a new session the Terminal Sidebar should not be present
Assert.IsFalse(testRunner.WaitForName("Terminal Sidebar", 0.5), "Terminal Sidebar should not exist");
testRunner.SwitchToTerminalTab();
Assert.IsTrue(testRunner.WaitForName("TerminalWidget"), "Terminal Window should exists after Show Terminal button is clicked");
return Task.CompletedTask;
});
}
}
}

View file

@ -0,0 +1,668 @@
/*
Copyright (c) 2022, Lars Brubaker
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the FreeBSD Project.
*/
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MatterHackers.Agg;
using MatterHackers.Agg.UI;
using MatterHackers.DataConverters3D;
using MatterHackers.GuiAutomation;
using MatterHackers.MatterControl.CustomWidgets;
using MatterHackers.MatterControl.DataStorage;
using MatterHackers.MatterControl.DesignTools;
using MatterHackers.MatterControl.DesignTools.Operations;
using MatterHackers.MatterControl.PartPreviewWindow;
using MatterHackers.MatterControl.PrintQueue;
using MatterHackers.VectorMath;
using NUnit.Framework;
using TestInvoker;
namespace MatterHackers.MatterControl.Tests.Automation
{
[TestFixture, Category("MatterControl.UI.Automation"), Parallelizable(ParallelScope.Children)]
public class PartPreviewTests
{
[Test, ChildProcessTest]
public async Task CopyButtonMakesCopyOfPart()
{
await MatterControlUtilities.RunTest((testRunner) =>
{
testRunner.OpenPartTab();
testRunner.AddItemToBed();
// Get View3DWidget
var view3D = testRunner.GetWidgetByName("View3DWidget", out _, 3) as View3DWidget;
var scene = view3D.Object3DControlLayer.Scene;
testRunner.WaitForName("Calibration - Box.stl");
Assert.AreEqual(1, scene.Children.Count, "Should have 1 part before copy");
// Select scene object
testRunner.Select3DPart("Calibration - Box.stl")
// Click Copy button and count Scene.Children
.ClickByName("Duplicate Button")
.Assert(() => scene.Children.Count == 2, "Should have 2 parts after copy");
// Click Copy button a second time and count Scene.Children
testRunner.ClickByName("Duplicate Button");
testRunner.Assert(() => scene.Children.Count == 3, "Should have 3 parts after 2nd copy");
return Task.CompletedTask;
}, overrideWidth: 1300, maxTimeToRun: 60);
}
[Test, ChildProcessTest]
public async Task AddMultiplePartsMultipleTimes()
{
await MatterControlUtilities.RunTest((testRunner) =>
{
testRunner.OpenPartTab();
var parts = new[]
{
"Row Item Cone",
"Row Item Sphere",
"Row Item Torus"
};
testRunner.AddPrimitivePartsToBed(parts, multiSelect: true);
var view3D = testRunner.GetWidgetByName("View3DWidget", out _, 3) as View3DWidget;
var scene = view3D.Object3DControlLayer.Scene;
testRunner.WaitForName("Selection");
Assert.AreEqual(1, scene.Children.Count, $"Should have 1 scene item after first AddToBed");
testRunner.ClickByName("Print Library Overflow Menu");
testRunner.ClickByName("Add to Bed Menu Item");
testRunner.WaitForName("Selection");
Assert.AreEqual(parts.Length + 1, scene.Children.Count, $"Should have {parts.Length + 1} scene items after second AddToBed");
return Task.CompletedTask;
}, overrideWidth: 1300, maxTimeToRun: 60);
}
[Test, ChildProcessTest]
public async Task AddingImageConverterWorks()
{
await MatterControlUtilities.RunTest((testRunner) =>
{
testRunner.OpenPartTab();
testRunner.AddItemToBed("Primitives Row Item Collection", "Row Item Image Converter");
var view3D = testRunner.GetWidgetByName("View3DWidget", out _, 3) as View3DWidget;
var scene = view3D.Object3DControlLayer.Scene;
testRunner.Assert(() => scene.Children.Count == 1, $"Should have 1 scene item after first AddToBed")
.Assert(() => scene.Descendants().Where(i => i is ImageObject3D).Any(), $"Should have 1 scene item after first AddToBed");
return Task.CompletedTask;
}, overrideWidth: 1300, maxTimeToRun: 60);
}
// NOTE: On GLFW, this test appears to fail due to the (lack of) behavior in PressModifierKeys.
[Test, ChildProcessTest]
public static async Task ControlClickInDesignTreeView()
{
await MatterControlUtilities.RunTest((testRunner) =>
{
testRunner.OpenPartTab();
var parts = new[]
{
"Row Item Cube",
"Row Item Half Cylinder",
"Row Item Half Wedge",
"Row Item Pyramid"
};
testRunner.AddPrimitivePartsToBed(parts, multiSelect: false);
var view3D = testRunner.GetWidgetByName("View3DWidget", out _, 3) as View3DWidget;
var scene = view3D.Object3DControlLayer.Scene;
var designTree = testRunner.GetWidgetByName("DesignTree", out _, 3) as TreeView;
Assert.AreEqual(scene.Children.Count, FetchTreeNodes().Count, "Scene part count should equal tree node count");
// Open up some room in the design tree view panel for adding group and selection nodes.
var splitter = designTree.Parents<Splitter>().First();
var splitterBar = splitter.Children.Where(child => child.GetType().Name == "SplitterBar").First();
var treeNodes = FetchTreeNodes();
var cubeNode = treeNodes.Where(node => ((IObject3D)node.Tag).Name == "Cube").Single();
var expandBy = cubeNode.Size.Y * 4d;
testRunner.DragWidget(splitterBar, new Point2D(0, expandBy)).Drop();
//===========================================================================================//
// Verify control-click isn't broken in library view.
var moreParts = new[]
{
"Row Item Sphere",
"Row Item Wedge"
};
testRunner.AddPrimitivePartsToBed(moreParts, multiSelect: true);
var partCount = parts.Length + moreParts.Length;
Assert.IsTrue(scene.Children.Any(child => child is SelectionGroupObject3D), "Scene should have a selection child");
treeNodes = FetchTreeNodes();
Assert.IsFalse(treeNodes.Where(node => node.Tag is SelectionGroupObject3D).Any());
Assert.AreEqual(treeNodes.Count, partCount, "Design tree should show all parts");
Assert.AreEqual(
scene.Children.Sum(child =>
{
if (child is SelectionGroupObject3D selection)
{
return selection.Children.Count;
}
return 1;
}),
treeNodes.Count,
"Number of parts in scene should equal number of nodes in design view");
//===========================================================================================//
// Verify rectangle drag select on bed creates a selection group in scene only.
//
// Rotate bed to top-down view so it's easier to select parts.
var top = Matrix4X4.LookAt(Vector3.Zero, new Vector3(0, 0, -1), new Vector3(0, 1, 0));
view3D.TrackballTumbleWidget.AnimateRotation(top);
testRunner.Delay()
.ClickByName("Pyramid")
.Delay()
.RectangleSelectParts(view3D.Object3DControlLayer, new[] { "Cube", "Pyramid" })
.Delay();
Assert.AreEqual(partCount - 3, scene.Children.Count, "Scene should have {0} children after drag rectangle select", partCount - 3);
Assert.IsTrue(scene.Children.Any(child => child is SelectionGroupObject3D), "Scene should have a selection child after drag rectangle select");
Assert.AreEqual(4, scene.SelectedItem.Children.Count, "4 parts should be selected");
Assert.IsTrue(
new HashSet<string>(scene.SelectedItem.Children.Select(child => child.Name)).SetEquals(new[] { "Cube", "Half Wedge", "Half Cylinder", "Pyramid" }),
"Cube, Half Cylinder, Half Wedge, Pyramid should be selected");
treeNodes = FetchTreeNodes();
Assert.IsFalse(treeNodes.Where(node => node.Tag is SelectionGroupObject3D).Any());
Assert.AreEqual(
scene.Children.Sum(child =>
{
if (child is SelectionGroupObject3D selection)
{
return selection.Children.Count;
}
return 1;
}),
treeNodes.Count,
"Number of parts in scene should equal number of nodes in design view afte drag rectangle select");
//===========================================================================================//
// Verify shift-clicking on parts on bed creates a selection group.
testRunner
.ClickByName("Sphere")
.ClickByName("Half Cylinder")
.PressModifierKeys(AutomationRunner.ModifierKeys.Shift)
.ClickByName("Pyramid")
.ReleaseModifierKeys(AutomationRunner.ModifierKeys.Shift);
Assert.AreEqual(partCount - 1, scene.Children.Count, "Should have {0} children after selection", partCount - 1);
Assert.IsTrue(scene.Children.Any(child => child is SelectionGroupObject3D), "Selection group should be child of scene");
Assert.IsFalse(scene.Children.Any(child => child.Name == "Half Cylinder" || child.Name == "Pyramid"), "Half Cylinder and Pyramid should be removed as direct children of scene");
Assert.IsNull(designTree.SelectedNode, "Design tree shouldn't have a selected node when multiple parts are selected");
//===========================================================================================//
// Verify grouping parts creates a group.
testRunner.ClickByName("Group Button");
Assert.AreEqual(partCount - 1, scene.Children.Count, "Should have {0} parts after group", partCount - 1);
Assert.IsInstanceOf<GroupHolesAppliedObject3D>(scene.SelectedItem, "Scene selection should be group");
Assert.IsInstanceOf<GroupHolesAppliedObject3D>(designTree.SelectedNode.Tag, "Group should be selected in design tree");
Assert.AreSame(scene.SelectedItem, designTree.SelectedNode.Tag, "Same group object should be selected in scene and design tree");
treeNodes = FetchTreeNodes();
Assert.AreEqual(scene.Children.Count, treeNodes.Count, "Scene part count should equal tree node count after group");
Assert.IsTrue(treeNodes.Any(node => node.Tag is GroupHolesAppliedObject3D), "Design tree should have node for group");
Assert.AreSame(designTree.SelectedNode.Tag, treeNodes.Single(node => node.Tag is GroupHolesAppliedObject3D).Tag, "Selected node in design tree should be group node");
var groupNode = treeNodes.Where(node => node.Tag is GroupHolesAppliedObject3D).Single();
Assert.AreEqual(2, groupNode.Nodes.Count, "Group should have 2 parts");
Assert.IsTrue(
new HashSet<string>(groupNode.Nodes.Select(node => ((IObject3D)node.Tag).Name)).SetEquals(new[] {"Half Cylinder", "Pyramid"}),
"Half Cylinder and Pyramind should be grouped");
var singleItemNodes = treeNodes
.Where(node => !(node.Tag is GroupHolesAppliedObject3D))
.Where(node => !(node.Tag is SelectionGroupObject3D))
.ToList();
var singleItemNames = new HashSet<string>(singleItemNodes.Select(item => ((IObject3D)item.Tag).Name));
Assert.AreEqual(partCount - 2, singleItemNodes.Count, "There should be {0} single item nodes in the design tree", partCount - 2);
Assert.IsTrue(singleItemNames.SetEquals(new[] {"Cube", "Half Wedge", "Sphere", "Wedge"}), "Cube, Half Wedge, Sphere, Wedge should be single items");
//===========================================================================================//
// Verify using the design tree to create a selection group.
var halfWedgeNode = treeNodes.Where(node => ((IObject3D)node.Tag).Name == "Half Wedge").Single();
var sphereNode = treeNodes.Where(node => ((IObject3D)node.Tag).Name == "Sphere").Single();
testRunner.ClickWidget(halfWedgeNode)
.PressModifierKeys(AutomationRunner.ModifierKeys.Control)
.ClickWidget(sphereNode)
.ReleaseModifierKeys(AutomationRunner.ModifierKeys.Control);
Assert.AreEqual(partCount - 2, scene.Children.Count, "Should have {0} parts after selection", partCount - 2);
Assert.IsNull(designTree.SelectedNode, "Design tree shouldn't have a selected node after creating selection in design tree");
//===========================================================================================//
// Verify control-clicking a part in the group does not get added to the selection group. Only top-level nodes can be
// selected.
treeNodes = FetchTreeNodes();
groupNode = treeNodes.Where(node => node.Tag is GroupHolesAppliedObject3D).Single();
testRunner.PressModifierKeys(AutomationRunner.ModifierKeys.Control)
.ClickWidget(groupNode.Nodes.Last())
.ReleaseModifierKeys(AutomationRunner.ModifierKeys.Control);
Assert.AreEqual(
scene.Children.Sum(child =>
{
if (child is SelectionGroupObject3D selection)
{
return selection.Children.Count;
}
return 1;
}),
treeNodes.Count,
"Scene part count should equal design tree node count after control-click on group child");
Assert.IsInstanceOf<SelectionGroupObject3D>(scene.SelectedItem, "Selection shouldn't change after control-click on group child");
Assert.AreEqual(2, scene.SelectedItem.Children.Count, "Selection should have 2 parts after control-click on group child");
//===========================================================================================//
// Verify adding group to selection.
testRunner.PressModifierKeys(AutomationRunner.ModifierKeys.Control)
.ClickWidget(groupNode.TitleBar)
.ReleaseModifierKeys(AutomationRunner.ModifierKeys.Control);
Assert.AreEqual(partCount - 3, scene.Children.Count, "Scene should have {0} children after control-clicking group", partCount - 3);
Assert.IsInstanceOf<SelectionGroupObject3D>(scene.SelectedItem, "Selected item should be a selection group after control-clicking on group");
Assert.AreEqual(3, scene.SelectedItem.Children.Count, "Selection should have 3 items after control-clicking on group");
Assert.IsTrue(
new HashSet<string>(scene.SelectedItem.Children.Select(child => child.Name)).SetEquals(new[] {"Half Wedge", "Sphere", "Half Cylinder, Pyramid" }),
"Selection should have Group, Half Wedge, Sphere");
//===========================================================================================//
// Verify control-clicking on a part in the selection removes it from the selection.
treeNodes = FetchTreeNodes();
halfWedgeNode = treeNodes.Where(node => ((IObject3D)node.Tag).Name == "Half Wedge").Single();
testRunner.PressModifierKeys(AutomationRunner.ModifierKeys.Control)
.ClickWidget(halfWedgeNode)
.ReleaseModifierKeys(AutomationRunner.ModifierKeys.Control);
Assert.IsInstanceOf<SelectionGroupObject3D>(scene.SelectedItem, "Selection group should exist after removing a child");
Assert.AreEqual(2, scene.SelectedItem.Children.Count, "Selection should have 2 parts after removing a child");
Assert.IsTrue(
new HashSet<string>(scene.SelectedItem.Children.Select(child => child.Name)).SetEquals(new[] { "Half Cylinder, Pyramid", "Sphere"}),
"Group and Sphere should be in selection after removing a child");
//===========================================================================================//
// Verify control-clicking on second-to-last part in the selection removes it from the selection
// and destroys selection group.
treeNodes = FetchTreeNodes();
groupNode = treeNodes.Where(node => node.Tag is GroupHolesAppliedObject3D).Single();
sphereNode = treeNodes.Where(node => ((IObject3D)node.Tag).Name == "Sphere").Single();
testRunner.PressModifierKeys(AutomationRunner.ModifierKeys.Control)
.ClickWidget(sphereNode)
.ReleaseModifierKeys(AutomationRunner.ModifierKeys.Control);
treeNodes = FetchTreeNodes();
Assert.AreEqual(scene.Children.Count, treeNodes.Count, "Scene part count should equal design tree node count after removing penultimate child");
Assert.IsNotInstanceOf<SelectionGroupObject3D>(scene.SelectedItem, "Selection group shouldn't exist after removing penultimate child");
Assert.AreSame(groupNode.Tag, scene.SelectedItem, "Selection should be group after removing penultimate child");
//===========================================================================================//
// Verify control-clicking on a part in the group that's part of the selection doesn't change the selection.
halfWedgeNode = treeNodes.Where(node => ((IObject3D)node.Tag).Name == "Half Wedge").Single();
testRunner.PressModifierKeys(AutomationRunner.ModifierKeys.Control)
.ClickWidget(halfWedgeNode)
.ReleaseModifierKeys(AutomationRunner.ModifierKeys.Control);
treeNodes = FetchTreeNodes();
sphereNode = treeNodes.Where(node => ((IObject3D)node.Tag).Name == "Sphere").Single();
testRunner.PressModifierKeys(AutomationRunner.ModifierKeys.Control)
.ClickWidget(sphereNode)
.ReleaseModifierKeys(AutomationRunner.ModifierKeys.Control);
treeNodes = FetchTreeNodes();
groupNode = treeNodes.Where(node => node.Tag is GroupHolesAppliedObject3D).Single();
testRunner.PressModifierKeys(AutomationRunner.ModifierKeys.Control)
.ClickWidget(groupNode.Nodes.Last())
.ReleaseModifierKeys(AutomationRunner.ModifierKeys.Control);
Assert.IsInstanceOf<SelectionGroupObject3D>(scene.SelectedItem, "Selection shouldn't change after control-click on selection group child");
Assert.AreEqual(3, scene.SelectedItem.Children.Count, "Selection should have 3 parts after control-click on selection group child");
//===========================================================================================//
// Verify clicking on a top-level node that's not in the selection group unselects all the parts in the group
// and selects the part associated with the clicked node.
treeNodes = FetchTreeNodes();
var wedgeNode = treeNodes.Where(node => ((IObject3D)node.Tag).Name == "Wedge").Single();
testRunner.ClickWidget(wedgeNode);
Assert.AreEqual(partCount - 1, scene.Children.Count, "Should be {0} parts in the scene after selecting wedge", partCount - 1);
Assert.AreSame(scene.SelectedItem, wedgeNode.Tag, "Wedge should be selected");
Assert.IsFalse(scene.Children.Any(child => child is SelectionGroupObject3D), "Selection group should go away when another part is selected");
Assert.AreSame(scene.SelectedItem, designTree.SelectedNode.Tag, "The same part should be selected in the scene and design tree");
treeNodes = FetchTreeNodes();
wedgeNode = treeNodes.Where(node => ((IObject3D)node.Tag).Name == "Wedge").Single();
Assert.AreSame(designTree.SelectedNode, wedgeNode, "Wedge node should be selected in design tree");
Assert.IsFalse(treeNodes.Any(node => node.Tag is SelectionGroupObject3D), "Selection group shouldn't exist in design tree after selecting wedge");
//===========================================================================================//
// Verify that shift-clicking a part on the bed makes a selection group with a part that's been selected through
// the design tree.
testRunner.PressModifierKeys(AutomationRunner.ModifierKeys.Shift)
.ClickByName("Half Wedge")
.ReleaseModifierKeys(AutomationRunner.ModifierKeys.Shift);
Assert.AreEqual(partCount - 2, scene.Children.Count, "Scene should have {0} children after selecting half wedge", partCount - 2);
Assert.IsNull(designTree.SelectedNode, "Selected node in design tree should be null after selecting half wedge");
Assert.IsInstanceOf<SelectionGroupObject3D>(scene.SelectedItem, "Should have a selection group after selecting half wedge");
Assert.IsTrue(
new HashSet<string>(scene.SelectedItem.Children.Select(child => child.Name)).SetEquals(new [] {"Wedge", "Half Wedge"}),
"Half Wedge and Wedge should be in selection");
//===========================================================================================//
// Verify that control-click on a top-level part adds to an existing selection.
treeNodes = FetchTreeNodes();
sphereNode = treeNodes.Where(node => ((IObject3D)node.Tag).Name == "Sphere").Single();
testRunner.PressModifierKeys(AutomationRunner.ModifierKeys.Control)
.ClickWidget(sphereNode)
.ReleaseModifierKeys(AutomationRunner.ModifierKeys.Control);
Assert.AreEqual(partCount - 3, scene.Children.Count, "Scene should have {0} children after selecting sphere", partCount - 3);
Assert.IsInstanceOf<SelectionGroupObject3D>(scene.SelectedItem, "Selection in scene should be selection group after adding sphere");
Assert.IsTrue(
new HashSet<string>(scene.SelectedItem.Children.Select(child => child.Name)).SetEquals(new [] {"Wedge", "Half Wedge", "Sphere"}),
"Half Wedge, Sphere, Wedge should be in selection");
//===========================================================================================//
// Done
return Task.CompletedTask;
// The nodes in the design tree are regenerated after certain events and must
// be fetched anew.
List<TreeNode> FetchTreeNodes() =>
designTree.Children
.Where(child => child is ScrollingArea)
.First()
.Children
.Where(child => child is FlowLayoutWidget)
.First()
.Children
.Select(child => (TreeNode)child)
.ToList();
}, overrideWidth: 1300, maxTimeToRun: 110);
}
[Test, ChildProcessTest]
public async Task DesignTabFileOperations()
{
await MatterControlUtilities.RunTest((testRunner) =>
{
testRunner.OpenPartTab(false);
// Get View3DWidget
var view3D = testRunner.GetWidgetByName("View3DWidget", out SystemWindow systemWindow, 3) as View3DWidget;
var scene = view3D.Object3DControlLayer.Scene;
var tempFilaname = "Temp Test Save.mcx";
var tempFullPath = Path.Combine(ApplicationDataStorage.Instance.DownloadsDirectory, tempFilaname);
// delete the temp file if it exists in the Downloads folder
void DeleteTempFile()
{
if (File.Exists(tempFullPath))
{
File.Delete(tempFullPath);
}
}
DeleteTempFile();
testRunner.Assert(() => scene.Children.Count == 1, "Should have 1 part (the phil)")
// Make sure the tab is named 'New Design'
.Assert(() => systemWindow.GetVisibleWigetWithText("New Design") != null, "Must have New Design")
// add a new part to the bed
.AddItemToBed()
// Click the save button
.ClickByName("Save")
// Cancle the save as
.ClickByName("Cancel Wizard Button")
// Make sure the tab is named 'New Design'
.Assert(() => systemWindow.GetVisibleWigetWithText("New Design") != null, "Have a new design tab")
// Click the close tab button
.ClickByName("Close Tab Button")
// Select Cancel
.ClickByName("Cancel Button")
// Make sure the tab is named 'New Design'
.Assert(() => systemWindow.GetVisibleWigetWithText("New Design") != null, "Still have design tab")
// Click the close tab button
.ClickByName("Close Tab Button")
// Select 'Save'
.ClickByName("Yes Button")
// Cancel the 'Save As'
.ClickByName("Cancel Wizard Button")
// Make sure the window is still open and the tab is named 'New Design'
.Assert(() => systemWindow.GetVisibleWigetWithText("New Design") != null, "still have desin tab")
// Click the save button
.ClickByName("Save")
// Save a temp file to the downloads folder
.DoubleClickByName("Computer Row Item Collection")
.DoubleClickByName("Downloads Row Item Collection")
.ClickByName("Design Name Edit Field")
.Type(tempFilaname)
.ClickByName("Accept Button")
// Verify it is there
.Assert(() => File.Exists(tempFullPath), "Must save the file")
// And that the tab got the name
.Assert(() => systemWindow.GetVisibleWigetWithText(tempFilaname) != null, "Tab was renamed")
// and the tooltip is right
.Assert(() => systemWindow.GetVisibleWigetWithText(tempFilaname).ToolTipText == tempFullPath, "Correct tool tip name")
// Add a part to the bed
.AddItemToBed()
// Click the close tab button (we have an edit so it should show the save request)
.ClickByName("Close Tab Button")
// Click the 'Cancel'
.ClickByName("Cancel Button")
// Click the 'Save' button
.ClickByName("Save")
// Click the close button (now we have no edit it should cancel without request)
.ClickByName("Close Tab Button");
// Verify the tab closes without requesting save
testRunner.Assert(() => systemWindow.GetVisibleWigetWithText(tempFilaname) == null, "The tab should have closed");
// delete the temp file if it exists in the Downloads folder
DeleteTempFile();
return Task.CompletedTask;
}, maxTimeToRun: 60);
}
[Test, ChildProcessTest]
public async Task GroupAndUngroup()
{
await MatterControlUtilities.RunTest((testRunner) =>
{
testRunner.OpenPartTab();
testRunner.AddItemToBed();
// Get View3DWidget and count Scene.Children before Copy button is clicked
View3DWidget view3D = testRunner.GetWidgetByName("View3DWidget", out _, 3) as View3DWidget;
var scene = view3D.Object3DControlLayer.Scene;
// Assert expected start count
Assert.AreEqual(1, scene.Children.Count, "Should have one part before copy");
// Select scene object
testRunner.Select3DPart("Calibration - Box.stl");
for (int i = 2; i <= 6; i++)
{
testRunner.ClickByName("Duplicate Button")
.Assert(() => scene.Children.Count == i, $"Should have {i} parts after copy");
}
// Get MeshGroupCount before Group is clicked
Assert.AreEqual(6, scene.Children.Count, "Scene should have 6 parts after copy loop");
// Duplicate button moved to new container - move focus back to View3DWidget so CTRL-A below is seen by expected control
testRunner.Select3DPart("Calibration - Box.stl")
// select all
.Type("^a")
.ClickByName("Group Button")
.Assert(() => scene.Children.Count == 1, $"Should have 1 parts after group");
testRunner.ClickByName("Ungroup Button")
.Assert(() => scene.Children.Count == 6, $"Should have 6 parts after ungroup");
return Task.CompletedTask;
}, overrideWidth: 1300);
}
[Test, ChildProcessTest]
public async Task RemoveButtonRemovesParts()
{
await MatterControlUtilities.RunTest((testRunner) =>
{
testRunner.OpenPartTab();
testRunner.AddItemToBed();
var view3D = testRunner.GetWidgetByName("View3DWidget", out _) as View3DWidget;
var scene = view3D.Object3DControlLayer.Scene;
testRunner.Select3DPart("Calibration - Box.stl");
Assert.AreEqual(1, scene.Children.Count, "There should be 1 part on the bed after AddDefaultFileToBedplate()");
// Add 5 items
for (int i = 0; i <= 4; i++)
{
testRunner.ClickByName("Duplicate Button")
.Delay(.5);
}
Assert.AreEqual(6, scene.Children.Count, "There should be 6 parts on the bed after the copy loop");
// Remove an item
testRunner.ClickByName("Remove Button");
// Confirm
Assert.AreEqual(5, scene.Children.Count, "There should be 5 parts on the bed after remove");
return Task.CompletedTask;
});
}
[Test, ChildProcessTest]
public async Task SaveAsToQueue()
{
await MatterControlUtilities.RunTest((testRunner) =>
{
testRunner.AddAndSelectPrinter();
testRunner.AddItemToBed();
var view3D = testRunner.GetWidgetByName("View3DWidget", out _) as View3DWidget;
testRunner.Select3DPart("Calibration - Box.stl");
int expectedCount = QueueData.Instance.ItemCount + 1;
testRunner.SaveBedplateToFolder("Test PartA.mcx", "Queue Row Item Collection")
.NavigateToLibraryHome()
.NavigateToFolder("Queue Row Item Collection");
Assert.IsTrue(testRunner.WaitForName("Row Item Test PartA.mcx"), "The part we added should be in the library");
Assert.AreEqual(expectedCount, QueueData.Instance.ItemCount, "Queue count should increase by one after Save operation");
return Task.CompletedTask;
});
}
[Test, ChildProcessTest]
public async Task SaveAsToLocalLibrary()
{
await MatterControlUtilities.RunTest((testRunner) =>
{
testRunner.AddAndSelectPrinter();
testRunner.AddItemToBed();
var view3D = testRunner.GetWidgetByName("View3DWidget", out _) as View3DWidget;
testRunner.Select3DPart("Calibration - Box.stl");
int expectedCount = QueueData.Instance.ItemCount + 1;
testRunner.SaveBedplateToFolder("Test PartB", "Local Library Row Item Collection")
.NavigateToLibraryHome()
.NavigateToFolder("Local Library Row Item Collection");
Assert.IsTrue(testRunner.WaitForName("Row Item Test PartB"), "The part we added should be in the library");
return Task.CompletedTask;
});
}
}
public static class WidgetExtensions
{
/// <summary>
/// Search the widget stack for a widget that is both visible on screen and has it's text set to the visibleText string
/// </summary>
/// <param name="widget">The root widget to search</param>
/// <param name="">the name to search for</param>
/// <returns></returns>
public static GuiWidget GetVisibleWigetWithText(this GuiWidget widget, string visibleText)
{
if (widget.ActuallyVisibleOnScreen())
{
if (widget.Text == visibleText)
{
return widget;
}
foreach(var child in widget.Children)
{
var childWithText = GetVisibleWigetWithText(child, visibleText);
if (childWithText != null)
{
return childWithText;
}
}
}
return null;
}
}
}

View file

@ -0,0 +1,278 @@
/*
Copyright (c) 2017, Lars Brubaker, John Lewin
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the FreeBSD Project.
*/
using System.Threading.Tasks;
using MatterHackers.MatterControl.PartPreviewWindow;
using NUnit.Framework;
using TestInvoker;
namespace MatterHackers.MatterControl.Tests.Automation
{
// Most of these tests are disabled. Local Library and Queue needs to be added by InitializeLibrary() (MatterHackers.MatterControl.ApplicationController).
[TestFixture, Category("MatterControl.UI.Automation"), Parallelizable(ParallelScope.Children)]
public class PrintQueueTests
{
[Test, ChildProcessTest]
public async Task AddOneItemToQueue()
{
await MatterControlUtilities.RunTest((testRunner) =>
{
// Expected = initial + 1
int expectedCount = QueueData.Instance.ItemCount + 1;
testRunner.AddAndSelectPrinter();
testRunner.ChangeToQueueContainer();
// Click Add button and select files
testRunner.InvokeLibraryAddDialog();
// Open Fennec_Fox
testRunner.CompleteDialog(MatterControlUtilities.GetTestItemPath("Fennec_Fox.stl"));
// Wait for expected outcome
testRunner.WaitFor(() => QueueData.Instance.ItemCount == expectedCount);
// Assert - one part added and queue count increases by one
Assert.AreEqual(expectedCount, QueueData.Instance.ItemCount, "Queue count should increase by 1 when adding 1 item");
Assert.IsTrue(testRunner.WaitForName("Row Item Fennec_Fox.stl"), "Named widget should exist after add(Fennec_Fox)");
return Task.CompletedTask;
});
}
[Test, ChildProcessTest]
public async Task AddTwoItemsToQueue()
{
await MatterControlUtilities.RunTest((testRunner) =>
{
// Expected = initial + 2;
int expectedCount = QueueData.Instance.ItemCount + 2;
testRunner.AddAndSelectPrinter();
testRunner.ChangeToQueueContainer();
// Click Add button and select files
testRunner.InvokeLibraryAddDialog();
// Open Fennec_Fox, Batman files
testRunner.CompleteDialog(
string.Format(
"\"{0}\";\"{1}\"",
MatterControlUtilities.GetTestItemPath("Fennec_Fox.stl"),
MatterControlUtilities.GetTestItemPath("Batman.stl")),
secondsToWait: 2);
// Wait for expected outcome
testRunner.WaitFor(() => QueueData.Instance.ItemCount == expectedCount);
// Assert - two parts added and queue count increases by two
Assert.AreEqual(expectedCount, QueueData.Instance.ItemCount, "Queue count should increase by 2 when adding 2 items");
Assert.IsTrue(testRunner.WaitForName("Row Item Fennec_Fox.stl"), "Named widget should exist after add(Fennec_Fox)");
Assert.IsTrue(testRunner.WaitForName("Row Item Batman.stl"), "Named widget should exist after add(Batman)");
return Task.CompletedTask;
});
}
[Test, ChildProcessTest]
public async Task RemoveButtonRemovesSingleItem()
{
await MatterControlUtilities.RunTest((testRunner) =>
{
int expectedCount = QueueData.Instance.ItemCount - 1;
testRunner.AddAndSelectPrinter();
testRunner.NavigateToFolder("Queue Row Item Collection");
// Select both items
testRunner.SelectListItems("Row Item 2013-01-25_Mouthpiece_v2.stl");
// Remove item
testRunner.LibraryRemoveSelectedItem();
testRunner.WaitFor(() => QueueData.Instance.ItemCount == expectedCount, 500);
Assert.AreEqual(expectedCount, QueueData.Instance.ItemCount, "Queue count should decrease by one after clicking Remove");
// Make sure selected item was removed
Assert.IsFalse(testRunner.WaitForName("Row Item 2013-01-25_Mouthpiece_v2.stl", .5), "Mouthpiece part should *not* exist after remove");
return Task.CompletedTask;
}, queueItemFolderToAdd: QueueTemplate.Three_Queue_Items);
}
[Test, ChildProcessTest]
public async Task RemoveButtonRemovesMultipleItems()
{
await MatterControlUtilities.RunTest((testRunner) =>
{
int expectedCount = QueueData.Instance.ItemCount - 2;
testRunner.AddAndSelectPrinter();
testRunner.NavigateToFolder("Queue Row Item Collection");
// Select both items
testRunner.SelectListItems("Row Item Batman.stl", "Row Item 2013-01-25_Mouthpiece_v2.stl");
// Remove items
testRunner.LibraryRemoveSelectedItem();
testRunner.WaitFor(() => QueueData.Instance.ItemCount == expectedCount, 500);
Assert.AreEqual(expectedCount, QueueData.Instance.ItemCount, "Queue count should decrease by two after clicking Remove");
// Make sure both selected items are removed
Assert.IsFalse(testRunner.WaitForName("Row Item Batman.stl", .5), "Batman part should *not* exist after remove");
Assert.IsFalse(testRunner.WaitForName("Row Item 2013-01-25_Mouthpiece_v2.stl", .5), "Mouthpiece part should *not* exist after remove");
return Task.CompletedTask;
}, queueItemFolderToAdd: QueueTemplate.Three_Queue_Items);
}
[Test, ChildProcessTest]
public async Task DragTo3DViewAddsItem()
{
await MatterControlUtilities.RunTest((testRunner) =>
{
testRunner.OpenPartTab();
testRunner.AddTestAssetsToLibrary(new[] { "Batman.stl" });
var view3D = testRunner.GetWidgetByName("View3DWidget", out _) as View3DWidget;
var scene = view3D.Object3DControlLayer.Scene;
Assert.AreEqual(0, scene.Children.Count, "The scene should have zero items before drag/drop");
testRunner.DragDropByName("Row Item Batman", "Object3DControlLayer");
Assert.AreEqual(1, scene.Children.Count, "The scene should have one item after drag/drop");
testRunner.Delay(.2);
testRunner.DragDropByName("Row Item Batman", "Object3DControlLayer");
Assert.AreEqual(2, scene.Children.Count, "The scene should have two items after drag/drop");
return Task.CompletedTask;
}, queueItemFolderToAdd: QueueTemplate.Three_Queue_Items);
}
[Test, ChildProcessTest]
public async Task AddAmfFile()
{
await MatterControlUtilities.RunTest((testRunner) =>
{
// Expected = initial + 1
int expectedCount = QueueData.Instance.ItemCount + 1;
testRunner.AddAndSelectPrinter();
testRunner.ChangeToQueueContainer();
// Click Add button and select files
testRunner.InvokeLibraryAddDialog();
// Open Rook
testRunner.CompleteDialog(
MatterControlUtilities.GetTestItemPath("Rook.amf"));
// Wait for expected outcome
testRunner.WaitFor(() => QueueData.Instance.ItemCount == expectedCount);
// Assert - one part added and queue count increases by one
Assert.AreEqual(expectedCount, QueueData.Instance.ItemCount, "Queue count should increase by 1 when adding 1 item");
Assert.IsTrue(testRunner.WaitForName("Row Item Rook.amf"), "Named widget should exist after add(Rook)");
return Task.CompletedTask;
});
}
[Test, ChildProcessTest]
public async Task AddStlFile()
{
await MatterControlUtilities.RunTest((testRunner) =>
{
// Expected = initial + 1
int expectedCount = QueueData.Instance.ItemCount + 1;
testRunner.AddAndSelectPrinter();
testRunner.ChangeToQueueContainer();
// Click Add button and select files
testRunner.InvokeLibraryAddDialog();
// Open Batman
testRunner.CompleteDialog(
MatterControlUtilities.GetTestItemPath("Batman.stl"));
// Wait for expected outcome
testRunner.WaitFor(() => QueueData.Instance.ItemCount == expectedCount);
// Assert - one part added and queue count increases by one
Assert.AreEqual(expectedCount, QueueData.Instance.ItemCount, "Queue count should increase by 1 when adding 1 item");
Assert.IsTrue(testRunner.WaitForName("Row Item Batman.stl"), "Named widget should exist after add(Batman)");
return Task.CompletedTask;
});
}
[Test, ChildProcessTest]
public async Task AddGCodeFile()
{
await MatterControlUtilities.RunTest((testRunner) =>
{
// Expected = initial + 1
int expectedCount = QueueData.Instance.ItemCount + 1;
testRunner.AddAndSelectPrinter();
testRunner.ChangeToQueueContainer();
// Click Add button and select files
testRunner.InvokeLibraryAddDialog();
// Open chichen-itza_pyramid
testRunner.CompleteDialog(
MatterControlUtilities.GetTestItemPath("chichen-itza_pyramid.gcode"));
// Wait for expected outcome
testRunner.WaitFor(() => QueueData.Instance.ItemCount == expectedCount);
// Assert - one part added and queue count increases by one
Assert.AreEqual(expectedCount, QueueData.Instance.ItemCount, "Queue count should increase by 1 when adding 1 item");
Assert.IsTrue(testRunner.WaitForName("Row Item chichen-itza_pyramid.gcode"), "Named widget should exist after add(chichen-itza)");
return Task.CompletedTask;
});
}
}
}

View file

@ -0,0 +1,48 @@
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MatterHackers.Agg.UI;
using MatterHackers.GuiAutomation;
using MatterHackers.MatterControl.PrinterCommunication;
using MatterHackers.MatterControl.PrinterCommunication.Io;
using MatterHackers.MatterControl.SlicerConfiguration;
using MatterHackers.PrinterEmulator;
using MatterHackers.VectorMath;
using NUnit.Framework;
using static MatterHackers.MatterControl.PrinterCommunication.PrinterConnection;
using TestInvoker;
namespace MatterHackers.MatterControl.Tests.Automation
{
[TestFixture, Category("MatterControl.UI.Automation")]
public class PrinterConnectionTests
{
[Test, ChildProcessTest]
public async Task PrinterDisconnectedOnTabClose()
{
await MatterControlUtilities.RunTest((testRunner) =>
{
// Create and connect to Airwolf via emulator port
using (var emulator = testRunner.LaunchAndConnectToPrinterEmulator())
{
var printer = testRunner.FirstPrinter();
// Ensure connected
Assert.AreEqual(CommunicationStates.Connected, printer.Connection.CommunicationState, "Printer should be Connected after LaunchAndConnectToPrinterEmulator");
// Close Printer
testRunner.CloseFirstPrinterTab();
// Ensure disconnected
testRunner.WaitFor(() => printer.Connection.CommunicationState == PrinterCommunication.CommunicationStates.Disconnected);
Assert.AreEqual(CommunicationStates.Disconnected, printer.Connection.CommunicationState, "Printer should be Disconnected after closing printer tab");
Assert.AreEqual(0, ReadThread.NumRunning, "No ReadThread instances should be running when only printer Disconnected");
}
return Task.CompletedTask;
}, maxTimeToRun: 120);
}
}
}

View file

@ -0,0 +1,97 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MatterHackers.MatterControl.PartPreviewWindow;
using MatterHackers.MatterControl.SlicerConfiguration;
using NUnit.Framework;
using TestInvoker;
namespace MatterHackers.MatterControl.Tests.Automation
{
[TestFixture, Category("MatterControl.UI.Automation"), Parallelizable(ParallelScope.Children)]
public class PrinterNameChangeTests
{
[Test, ChildProcessTest]
public async Task NameChangeOnlyEffectsOnePrinter()
{
await MatterControlUtilities.RunTest((testRunner) =>
{
testRunner.WaitForFirstDraw();
// Add Guest printers
testRunner.AddAndSelectPrinter("Airwolf 3D", "HD");
var printer1 = testRunner.FirstPrinter();
testRunner.AddAndSelectPrinter("BCN3D", "Sigma");
var printer2 = ApplicationController.Instance.ActivePrinters.Last();
string newName0 = "Updated name 0";
string newName1 = "Updated name 1";
var printerTab0 = testRunner.GetWidgetByName("3D View Tab 0", out _) as ChromeTab;
var printerTab1 = testRunner.GetWidgetByName("3D View Tab 1", out _) as ChromeTab;
// switch back to airwolf tab
testRunner.ClickByName("3D View Tab 0")
.SwitchToPrinterSettings()
.InlineTitleEdit("Printer Name", newName0);
Assert.AreEqual(newName0, printerTab0.Text);
Assert.AreEqual("BCN3D Sigma", printerTab1.Text);
// switch back to BCN tab
testRunner.ClickByName("3D View Tab 1")
.SwitchToPrinterSettings()
.InlineTitleEdit("Printer Name", newName1);
Assert.AreEqual(newName1, printerTab1.Text);
Assert.AreEqual(newName0, printerTab0.Text, "Name did not change");
return Task.CompletedTask;
}, maxTimeToRun: 120);
}
[Test, ChildProcessTest]
public async Task NameChangePersists()
{
// Ensures that printer model changes are applied correctly and observed by the view
await MatterControlUtilities.RunTest((testRunner) =>
{
testRunner.WaitForFirstDraw();
testRunner.AddAndSelectPrinter("Airwolf 3D", "HD");
Assert.AreEqual(1, ApplicationController.Instance.ActivePrinters.Count(), "One printer should exist after add");
testRunner.SwitchToPrinterSettings();
// Change the printer name
string newName = "Updated name";
testRunner.InlineTitleEdit("Printer Name", newName);
var printer = testRunner.FirstPrinter();
string printerID = printer.Settings.ID;
// Wait for change
testRunner.WaitFor(() => newName == ProfileManager.Instance[printerID].Name);
// Validate that the model reflects the new name
Assert.AreEqual(newName, ProfileManager.Instance[printerID].Name, "ActiveProfile has updated name");
// Validate that the treeview reflects the new name
testRunner.SwitchToHardwareTab();
Assert.IsTrue(testRunner.WaitForName(newName + " Node"), "Widget with updated printer name exists");
// Validate that the tab reflects the new name
var printerTab = testRunner.GetWidgetByName("3D View Tab 0", out _) as ChromeTab;
Assert.AreEqual(newName, printerTab.Text);
// Validate that the settings layer reflects the new name
Assert.AreEqual(newName, printer.PrinterName);
return Task.CompletedTask;
});
}
}
}

View file

@ -0,0 +1,799 @@
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MatterHackers.Agg;
using MatterHackers.Agg.UI;
using MatterHackers.GuiAutomation;
using MatterHackers.MatterControl.PrinterCommunication;
using MatterHackers.MatterControl.PrinterCommunication.Io;
using MatterHackers.MatterControl.PrintHistory;
using MatterHackers.MatterControl.SlicerConfiguration;
using MatterHackers.PrinterEmulator;
using MatterHackers.VectorMath;
using NUnit.Framework;
using TestInvoker;
namespace MatterHackers.MatterControl.Tests.Automation
{
[TestFixture, Category("MatterControl.UI.Automation"), Parallelizable(ParallelScope.Children)]
public class PrintingTests
{
[Test, ChildProcessTest, Category("Emulator")]
public async Task CompletingPrintTurnsoffHeat()
{
await MatterControlUtilities.RunTest((testRunner) =>
{
testRunner.WaitForName("Cancel Wizard Button", 1);
using (var emulator = testRunner.LaunchAndConnectToPrinterEmulator())
{
Assert.AreEqual(1, ApplicationController.Instance.ActivePrinters.Count(), "One printer should be defined after add");
testRunner.SelectSliceSettingsField(SettingsKey.end_gcode);
testRunner.Type("^a");
testRunner.Type("{BACKSPACE}");
testRunner.Type("G28");
testRunner.SelectSliceSettingsField(SettingsKey.start_gcode);
var printer = testRunner.FirstPrinter();
// TODO: Failure persisting GCode / MultilineTextField value
// Expected string length 3 but was 2.Strings differ at index 0.
// Shift-G is being swallowed by something.
// Validate GCode fields persist values
Assert.AreEqual(
"G28",
printer.Settings.GetValue(SettingsKey.end_gcode),
"Failure persisting GCode/MultilineTextField value");
testRunner.AddItemToBed();
// Shorten the delay so the test runs in a reasonable time
printer.Connection.TimeToHoldTemperature = 5;
testRunner.StartPrint(printer);
// Wait for print to finish
testRunner.WaitForPrintFinished(printer);
// Wait for expected temp
testRunner.WaitFor(() => printer.Connection.GetActualHotendTemperature(0) <= 0, 10);
Assert.Less(printer.Connection.GetActualHotendTemperature(0), 30);
// Wait for expected temp
testRunner.WaitFor(() => printer.Connection.ActualBedTemperature <= 10);
Assert.Less(printer.Connection.ActualBedTemperature, 10);
// Make sure we can run this whole thing again
testRunner.StartPrint(printer);
// Wait for print to finish
testRunner.WaitForPrintFinished(printer);
// Wait for expected temp
testRunner.WaitFor(() => printer.Connection.GetActualHotendTemperature(0) <= 0, 10);
Assert.Less(printer.Connection.GetActualHotendTemperature(0), 30);
// Wait for expected temp
testRunner.WaitFor(() => printer.Connection.ActualBedTemperature <= 10);
Assert.Less(printer.Connection.ActualBedTemperature, 10);
}
return Task.CompletedTask;
}, maxTimeToRun: 95);
}
[Test, ChildProcessTest, Category("Emulator")]
public async Task PulseLevelingTest()
{
// Validates the Pulse profile requires leveling and it works as expected
await MatterControlUtilities.RunTest((testRunner) =>
{
AutomationRunner.TimeToMoveMouse = .2;
testRunner.WaitForName("Cancel Wizard Button", 1);
using (var emulator = testRunner.LaunchAndConnectToPrinterEmulator("Pulse", "A-134"))
{
Assert.AreEqual(1, ApplicationController.Instance.ActivePrinters.Count(), "One printer should be defined after add");
testRunner.OpenPrintPopupMenu()
.ClickByName("SetupPrinter")
.Complete9StepLeveling()
.AddItemToBed();
var printer = testRunner.FirstPrinter();
var currentSettings = printer.Settings;
currentSettings.SetValue(SettingsKey.pause_gcode, "");
currentSettings.SetValue(SettingsKey.resume_gcode, "");
testRunner.StartPrint(printer, pauseAtLayers: "2");
testRunner.WaitForName("Yes Button", 20); // the yes button is 'Resume'
// the user types in the pause layer 1 based and we are 0 based, so we should be on: user 2, printer 1.
Assert.AreEqual(1, printer.Connection.CurrentlyPrintingLayer);
// assert the leveling is working
Assert.AreEqual(11.25, emulator.Destination.Z);
testRunner.CancelPrint();
// now run leveling again and make sure we get the same result
testRunner.SwitchToControlsTab();
testRunner.ClickByName("Printer Calibration Button");
testRunner.ClickByName("Print Leveling Row");
testRunner.Complete9StepLeveling(2);
testRunner.StartPrint(printer, pauseAtLayers: "2");
testRunner.WaitForName("Yes Button", 20); // the yes button is 'Resume'
// the user types in the pause layer 1 based and we are 0 based, so we should be on: user 2, printer 1.
Assert.AreEqual(1, printer.Connection.CurrentlyPrintingLayer);
// assert the leveling is working
Assert.AreEqual(12.25, emulator.Destination.Z);
// NOTE: System.Exception : WaitForWidgetEnabled Failed: Named GuiWidget not found [Print Progress Dial]
// Might be fixed in CancelPrint now.
testRunner.CancelPrint();
// now modify the leveling data manually and assert that it is applied when printing
testRunner.SwitchToControlsTab();
testRunner.ClickByName("Printer Calibration Button");
testRunner.ClickByName("Edit Leveling Data Button");
for (int i = 0; i < 3; i++)
{
var name = $"z Position {i}";
testRunner.ClickByName(name);
testRunner.Type("^a"); // select all
testRunner.Type("5");
}
testRunner.ClickByName("Save Leveling Button");
testRunner.ClickByName("Cancel Wizard Button");
testRunner.StartPrint(printer, pauseAtLayers: "2");
testRunner.WaitForName("Yes Button", 20); // the yes button is 'Resume'
// the user types in the pause layer 1 based and we are 0 based, so we should be on: user 2, printer 1.
Assert.AreEqual(1, printer.Connection.CurrentlyPrintingLayer);
// assert the leveling is working
Assert.AreEqual(5.25, emulator.Destination.Z);
}
return Task.CompletedTask;
}, maxTimeToRun: 230);
}
[Test, ChildProcessTest, Category("Emulator")]
public void ExpectedEmulatorResponses()
{
// TODO: Emulator behavior should emulate actual printer firmware and use configuration rather than M104/M109 sends to set extruder count
//
// Quirky emulator returns single extruder M105 responses until after the first M104, at which point it extends its extruder count to match
string M105ResponseBeforeM104 = "ok T:27.0 / 0.0";
string M105ResponseAfterM104 = "ok T0:27.0 / 0.0 T1:27.0 / 0.0";
string[] test1 = new string[]
{
"N1 M110 N1 * 125",
"ok",
"N2 M114 * 37",
"X:0.00 Y: 0.00 Z: 0.00 E: 0.00 Count X: 0.00 Y: 0.00 Z: 0.00",
"ok",
"N3 M105 * 36",
M105ResponseBeforeM104,
"N1 M110 N1*125",
"ok",
"N2 M115 * 36",
"MatterControl Printer Emulator",
"Commands:",
" SLOW // make the emulator simulate actual printing speeds (default)",
" FAST // run as fast as possible",
" THROWERROR // generate a simulated error for testing",
"Emulating:",
"FIRMWARE_NAME:Marlin V1; Sprinter/grbl mashup for gen6 FIRMWARE_URL:https://github.com/MarlinFirmware/Marlin PROTOCOL_VERSION:1.0 MACHINE_TYPE:Framelis v1 EXTRUDER_COUNT:1 UUID:155f84b5-d4d7-46f4-9432-667e6876f37a",
"ok",
"N3 M104 T0 S0 * 34",
"ok",
"N4 M104 T1 S0 * 36",
"ok",
"N5 M105 * 34",
M105ResponseAfterM104,
"N6 M105 * 45",
"Error:checksum mismatch, Last Line: 5",
"Resend: 6",
"ok",
"N6 M105 * 33",
M105ResponseAfterM104,
"N7 M105 * 32",
M105ResponseAfterM104,
"N8 M105 * 47",
M105ResponseAfterM104,
"N9 M105 * 46",
M105ResponseAfterM104,
"N10 M105 * 22",
M105ResponseAfterM104,
"N11 M105 * 23",
M105ResponseAfterM104,
"N12 M105 * 20",
M105ResponseAfterM104,
"N13 M105 * 21",
M105ResponseAfterM104,
"N14 M105 * 18",
M105ResponseAfterM104,
"N15 M105 * 19",
M105ResponseAfterM104,
"N16 M105 * 16",
M105ResponseAfterM104,
"N17 M105 * 40",
"Error:checksum mismatch, Last Line: 16",
"Resend: 17",
"ok",
"N17 M105 * 17",
M105ResponseAfterM104,
};
string[] test2 = new string[]
{
"N1 M110 N1*125",
"ok",
"N1 M110 N1*125",
"ok",
"N1 M110 N1*125",
"ok",
"N2 M114*37",
"X:0.00 Y: 0.00 Z: 0.00 E: 0.00 Count X: 0.00 Y: 0.00 Z: 0.00",
"ok",
};
SimulatePrint(test1);
SimulatePrint(test2);
}
private static void SimulatePrint(string[] sendRecieveLog)
{
using (var emulator = new Emulator())
{
emulator.HasHeatedBed = false;
int lineIndex = 0;
while (lineIndex < sendRecieveLog.Length)
{
var sentCommand = sendRecieveLog[lineIndex];
string response = emulator.GetCorrectResponse(sentCommand);
lineIndex++;
var lines = response.Split('\n');
for (int i = 0; i < lines.Length; i++)
{
if (!string.IsNullOrEmpty(lines[i]))
{
Assert.AreEqual(sendRecieveLog[lineIndex], lines[i]);
lineIndex++;
}
}
}
}
}
[Test, ChildProcessTest, Category("Emulator")]
public async Task PrinterRequestsResumeWorkingAsExpected()
{
await MatterControlUtilities.RunTest((testRunner) =>
{
using (var emulator = testRunner.LaunchAndConnectToPrinterEmulator())
{
Assert.AreEqual(1, ApplicationController.Instance.ActivePrinters.Count(), "One printer should be defined after add");
// print a part
testRunner.AddItemToBed();
testRunner.StartPrint(testRunner.FirstPrinter(), pauseAtLayers: "2;6");
// turn on line error simulation
emulator.SimulateLineErrors = true;
// close the pause dialog pop-up (resume)
testRunner.WaitForName("Yes Button", 20); // the yes button is 'Resume'
testRunner.ClickByName("Yes Button");
// simulate board reboot
emulator.SimulateReboot();
// close the pause dialog pop-up (resume)
testRunner.Delay(3);
testRunner.WaitForName("Yes Button", 20);
testRunner.ClickByName("Yes Button");
// Wait for done
testRunner.WaitForPrintFinished(testRunner.FirstPrinter());
}
return Task.CompletedTask;
}, maxTimeToRun: 90 * 2); // Once timed out at 90.
}
[Test, ChildProcessTest, Category("Emulator")]
public async Task PrinterDeletedWhilePrinting()
{
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();
// print a part
testRunner.AddItemToBed();
testRunner.StartPrint(printer, pauseAtLayers: "2");
ProfileManager.DebugPrinterDelete = true;
// Wait for pause dialog
testRunner.ClickResumeButton(printer, true, 1);
// Wait for done
testRunner.WaitForPrintFinished(printer);
}
return Task.CompletedTask;
}, maxTimeToRun: 180);
}
[Test, ChildProcessTest, Category("Emulator")]
public async Task PrinterRecoveryTest()
{
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.recover_is_enabled, "1");
printer.Settings.SetValue(SettingsKey.has_hardware_leveling, "0");
Assert.IsTrue(printer.Connection.RecoveryIsEnabled);
// print a part
testRunner.AddItemToBed()
.StartPrint(printer, pauseAtLayers: "2;4;6")
.ClickResumeButton(printer, true, 1) // Resume
.ClickResumeButton(printer, false, 3) // close the pause dialog pop-up do not resume
.ClickByName("Disconnect from printer button")
.ClickByName("Yes Button") // accept the disconnect
//.ClickByName("Cancel Wizard Button") // click the close on the collect info dialog
.ClickByName("Connect to printer button") // Reconnect
.WaitFor(() => printer.Connection.CommunicationState == CommunicationStates.Connected);
// Assert that recovery happens
Assert.IsTrue(PrintRecovery.RecoveryAvailable(printer), "Recovery should be enabled after Disconnect while printing");
// Recover the print
testRunner.ClickButton("Yes Button", "Recover Print")
.ClickResumeButton(printer, true, 5) // The first pause that we get after recovery should be layer 6.
.WaitForPrintFinished(printer);
}
return Task.CompletedTask;
}, maxTimeToRun: 180);
}
[Test, ChildProcessTest, Category("Emulator")]
public async Task TemperatureTowerWorks()
{
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();
bool foundTemp = false;
printer.Connection.LineSent += (s, e) =>
{
if (e.Contains("M104 S222.2"))
{
foundTemp = true;
}
};
// print a part
testRunner.AddItemToBed()
.AddItemToBed("Scripting Row Item Collection", "Row Item Set Temperature")
.DragDropByName("MoveInZControl", "MoveInZControl", offsetDrag: new Point2D(0, 0), offsetDrop: new Point2D(0, 10))
.ClickByName("Temperature Edit")
.Type("222.2")
.StartPrint(printer)
.WaitFor(() => printer.Connection.CommunicationState == CommunicationStates.FinishedPrint, 60);
// TODO: finish export test
//.ExportPrintAndLoadGCode(printer, out string gcode);
Assert.IsTrue(foundTemp);
}
return Task.CompletedTask;
}, maxTimeToRun: 180);
}
[Test, ChildProcessTest, Category("Emulator")]
public async Task RecoveryT1NoProbe()
{
await ExtruderT1RecoveryTest("Airwolf 3D", "HD");
}
[Test, ChildProcessTest, Category("Emulator")]
public async Task RecoveryT1WithProbe()
{
await ExtruderT1RecoveryTest("FlashForge", "Creator Dual");
}
public async Task ExtruderT1RecoveryTest(string make, string model)
{
await MatterControlUtilities.RunTest((testRunner) =>
{
using (var emulator = testRunner.LaunchAndConnectToPrinterEmulator(make, model))
{
Assert.AreEqual(1, ApplicationController.Instance.ActivePrinters.Count(), "One printer should exist after add");
var printer = testRunner.FirstPrinter();
testRunner.ChangeSettings(
new (string, string)[]
{
(SettingsKey.recover_is_enabled, "1"),
(SettingsKey.extruder_count, "2"),
(SettingsKey.has_hardware_leveling, "0"),
}, printer);
Assert.IsTrue(printer.Connection.RecoveryIsEnabled);
// print a part
testRunner.AddItemToBed()
.ClickByName("ItemMaterialButton")
.ClickByName("Material 2 Button")
.StartPrint(printer, pauseAtLayers: "2;3;4;5");
testRunner.ClickResumeButton(printer, true, 1); // Resume
// make sure we are printing with extruder 2 (T1)
Assert.AreEqual(0, printer.Connection.GetTargetHotendTemperature(0));
Assert.Greater(printer.Connection.GetTargetHotendTemperature(1), 0);
testRunner.ClickResumeButton(printer, false, 2) // close the pause dialog pop-up do not resume
.ClickByName("Disconnect from printer button")
.ClickByName("Yes Button") // Are you sure?
.ClickByName("Connect to printer button") // Reconnect
.WaitFor(() => printer.Connection.CommunicationState == CommunicationStates.Connected);
// Assert that recovery happens
Assert.IsTrue(PrintRecovery.RecoveryAvailable(printer), "Recovery should be enabled after Disconnect while printing");
// Recover the print
testRunner.ClickButton("Yes Button", "Recover Print");
// The first pause that we get after recovery should be layer 4 (index 3).
testRunner.ClickResumeButton(printer, true, 3);
// make sure we are printing with extruder 2 (T1)
Assert.AreEqual(0, printer.Connection.GetTargetHotendTemperature(0));
Assert.Greater(printer.Connection.GetTargetHotendTemperature(1), 0);
testRunner.ClickResumeButton(printer, true, 4);
testRunner.WaitForPrintFinished(printer);
}
return Task.CompletedTask;
}, maxTimeToRun: 180);
}
[Test, ChildProcessTest, Category("Emulator")]
public async Task TuningAdjustmentsDefaultToOneAndPersists()
{
double targetExtrusionRate = 1.5;
double targetFeedRate = 2;
await MatterControlUtilities.RunTest((testRunner) =>
{
testRunner.WaitForName("Cancel Wizard Button");
using (var emulator = testRunner.LaunchAndConnectToPrinterEmulator())
{
Assert.AreEqual(1, ApplicationController.Instance.ActivePrinters.Count(), "One printer should be defined after add");
testRunner.AddItemToBed();
testRunner.SwitchToControlsTab();
var printer = testRunner.FirstPrinter();
testRunner.StartPrint(printer)
.ScrollIntoView("Extrusion Multiplier NumberEdit")
.ScrollIntoView("Feed Rate NumberEdit");
testRunner.PausePrint();
// Tuning values should default to 1 when missing
ConfirmExpectedSpeeds(testRunner, 1, 1, "Initial case");
testRunner.Delay()
.ClickByName("Extrusion Multiplier NumberEdit")
.Type(targetExtrusionRate.ToString())
.ClickByName("Feed Rate NumberEdit")
.Type(targetFeedRate.ToString())
// Force focus away from the feed rate field, causing an persisted update
.ClickByName("Extrusion Multiplier NumberEdit");
ConfirmExpectedSpeeds(testRunner, targetExtrusionRate, targetFeedRate, "After setting TextEdit values");
// Wait for slicing to complete before setting target values
testRunner.WaitFor(() => printer.Connection.DetailedPrintingState == DetailedPrintingState.Printing, 8);
testRunner.Delay();
ConfirmExpectedSpeeds(testRunner, targetExtrusionRate, targetFeedRate, "While printing");
testRunner.ResumePrint();
// Wait up to 60 seconds for the print to finish
testRunner.WaitForPrintFinished(printer, 60);
// Values should match entered values
ConfirmExpectedSpeeds(testRunner, targetExtrusionRate, targetFeedRate, "After print finished");
testRunner.StartPrint(printer) // Restart the print
.Delay(1);
// Values should match entered values
ConfirmExpectedSpeeds(testRunner, targetExtrusionRate, targetFeedRate, "After print restarted");
testRunner.CancelPrint()
.Delay(1);
// Values should match entered values
ConfirmExpectedSpeeds(testRunner, targetExtrusionRate, targetFeedRate, "After canceled print");
}
return Task.CompletedTask;
}, overrideHeight: 900, maxTimeToRun: 120);
}
[Test, ChildProcessTest, Category("Emulator")]
public async Task TuningAdjustmentControlsBoundToStreamValues()
{
double targetExtrusionRate = 1.5;
double targetFeedRate = 2;
double initialExtrusionRate = 0.6;
double initialFeedRate = 0.7;
await MatterControlUtilities.RunTest((testRunner) =>
{
testRunner.WaitForName("Cancel Wizard Button");
// Then validate that they are picked up
using (var emulator = testRunner.LaunchAndConnectToPrinterEmulator())
{
Assert.AreEqual(1, ApplicationController.Instance.ActivePrinters.Count(), "One printer should be defined after add");
testRunner.AddItemToBed();
testRunner.SwitchToControlsTab();
var printer = testRunner.FirstPrinter();
// Set custom adjustment values
printer.Settings.SetValue(SettingsKey.feedrate_ratio, initialFeedRate.ToString());
printer.Settings.SetValue(SettingsKey.extrusion_ratio, initialExtrusionRate.ToString());
var printFinishedResetEvent = new AutoResetEvent(false);
printer.Connection.PrintFinished += (s, e) => printFinishedResetEvent.Set();
testRunner.StartPrint(printer);
testRunner.PausePrint();
var container = testRunner.GetWidgetByName("ManualPrinterControls.ControlsContainer", out _, 5);
// Scroll the widget into view
var scrollable = container.Parents<ManualPrinterControls>().FirstOrDefault() as ScrollableWidget;
var width = scrollable.Width;
// Workaround needed to scroll to the bottom of the Controls panel
// scrollable.ScrollPosition = new Vector2();
scrollable.ScrollPosition = new Vector2(0, 30);
// Workaround to force layout to fix problems with size of Tuning Widgets after setting ScrollPosition manually
scrollable.Width = width - 1;
scrollable.Width = width;
// Tuning values should match
ConfirmExpectedSpeeds(testRunner, initialExtrusionRate, initialFeedRate, "Initial case");
testRunner.Delay();
testRunner.ClickByName("Extrusion Multiplier NumberEdit");
testRunner.Type(targetExtrusionRate.ToString());
testRunner.ClickByName("Feed Rate NumberEdit");
testRunner.Type(targetFeedRate.ToString());
// Force focus away from the feed rate field, causing an persisted update
testRunner.ClickByName("Extrusion Multiplier NumberEdit");
ConfirmExpectedSpeeds(testRunner, targetExtrusionRate, targetFeedRate, "After setting TextEdit values");
// Wait for slicing to complete before setting target values
testRunner.WaitFor(() => printer.Connection.DetailedPrintingState == DetailedPrintingState.Printing, 8);
testRunner.Delay();
// Values should remain after print completes
ConfirmExpectedSpeeds(testRunner, targetExtrusionRate, targetFeedRate, "While printing");
testRunner.ResumePrint();
// Wait for printing to complete
printFinishedResetEvent.WaitOne();
testRunner.WaitForPrintFinished(printer);
// Values should match entered values
testRunner.StartPrint(printer);
testRunner.WaitFor(() => printer.Connection.CommunicationState == CommunicationStates.Printing, 15);
// Values should match entered values
ConfirmExpectedSpeeds(testRunner, targetExtrusionRate, targetFeedRate, "While reprinting");
testRunner.CancelPrint();
testRunner.WaitFor(() => printer.Connection.CommunicationState == CommunicationStates.Connected, 15);
// Values should match entered values
ConfirmExpectedSpeeds(testRunner, targetExtrusionRate, targetFeedRate, "After cancel");
}
return Task.CompletedTask;
}, overrideHeight: 900, maxTimeToRun: 120);
}
[Test, ChildProcessTest, Category("Emulator")]
public async Task CloseShouldNotStopSDPrint()
{
await MatterControlUtilities.RunTest((testRunner) =>
{
testRunner.WaitForName("Cancel Wizard Button");
using (var emulator = testRunner.LaunchAndConnectToPrinterEmulator(runSlow: true))
{
testRunner.NavigateToFolder("SD Card Row Item Collection");
testRunner.ClickByName("Row Item Item 1.gcode");
testRunner.ClickByName("Print Library Overflow Menu");
testRunner.ClickByName("Print Menu Item");
testRunner.Delay(2);
int tempChangedCount = 0;
int fanChangedCount = 0;
emulator.ExtruderTemperatureChanged += (s, e) =>
{
tempChangedCount++;
};
emulator.FanSpeedChanged += (s, e) =>
{
fanChangedCount++;
};
testRunner.CloseMatterControl();
testRunner.ClickByName("Yes Button");
testRunner.Delay(2);
Assert.AreEqual(0, tempChangedCount, "We should not change this while exiting an sd card print.");
Assert.AreEqual(0, fanChangedCount, "We should not change this while exiting an sd card print.");
}
return Task.CompletedTask;
}, maxTimeToRun: 90);
}
[Test, ChildProcessTest, Category("Emulator")]
public async Task CancelingPrintTurnsHeatAndFanOff()
{
await MatterControlUtilities.RunTest((testRunner) =>
{
testRunner.WaitForName("Cancel Wizard Button");
using (var emulator = testRunner.LaunchAndConnectToPrinterEmulator())
{
var resetEvent = new AutoResetEvent(false);
Assert.AreEqual(1, ApplicationController.Instance.ActivePrinters.Count(), "One printer should exist after add");
testRunner.AddItemToBed();
var printer = testRunner.FirstPrinter();
testRunner.StartPrint(printer);
int fanChangedCount = 0;
emulator.FanSpeedChanged += (s, e) =>
{
fanChangedCount++;
};
emulator.WaitForLayer(printer.Settings, 2);
emulator.RunSlow = true;
// Click close but cancel
testRunner.CloseMatterControl();
testRunner.ClickByName("No Button");
// Wait for close
testRunner.WaitForWidgetDisappear("Yes Button", 4);
testRunner.Delay(2);
// Confirm abort
Assert.IsFalse(AppContext.RootSystemWindow.HasBeenClosed, "Canceling Close dialog should *not* close MatterControl");
// Close MatterControl and cancel print
testRunner.CloseMatterControl();
testRunner.ClickByName("Yes Button");
// Wait for Disconnected CommunicationState which occurs after PrinterConnection.Disable()
testRunner.WaitForCommunicationStateDisconnected(printer, maxSeconds: 30);
// Wait for close
testRunner.WaitForWidgetDisappear("Yes Button", 4);
testRunner.Delay(2);
// Confirm close
Assert.IsTrue(AppContext.RootSystemWindow.HasBeenClosed, "Confirming Close dialog *should* close MatterControl");
// Wait for M106 change
testRunner.WaitFor(() => fanChangedCount > 0, 15, 500);
// Assert expected temp targets and fan transitions
Assert.AreEqual(0, (int) emulator.CurrentExtruder.TargetTemperature, "Unexpected target temperature - MC close should call Connection.Disable->TurnOffBedAndExtruders to shutdown heaters");
Assert.AreEqual(0, (int) emulator.HeatedBed.TargetTemperature, "Unexpected target temperature - MC close should call Connection.Disable->TurnOffBedAndExtruders to shutdown heaters");
Assert.AreEqual(1, fanChangedCount, "Unexpected fan speed change count - MC close should call Connection.Disable which shuts down fans via M106");
}
return Task.CompletedTask;
}, overrideHeight: 900, maxTimeToRun: 90);
}
private static void ConfirmExpectedSpeeds(AutomationRunner testRunner, double targetExtrusionRate, double targetFeedRate, string scope)
{
SolidSlider slider;
// Assert the UI has the expected values
slider = testRunner.GetWidgetByName("Extrusion Multiplier Slider", out _) as SolidSlider;
testRunner.WaitFor(() => targetExtrusionRate == slider.Value);
Assert.AreEqual(targetExtrusionRate, slider.Value, $"Unexpected Extrusion Rate Slider Value - {scope}");
slider = testRunner.GetWidgetByName("Feed Rate Slider", out _) as SolidSlider;
testRunner.WaitFor(() => targetFeedRate == slider.Value);
Assert.AreEqual(targetFeedRate, slider.Value, $"Unexpected Feed Rate Slider Value - {scope}");
var printer = testRunner.FirstPrinter();
// Assert the changes took effect on the model
testRunner.WaitFor(() => targetExtrusionRate == printer.Connection.ExtrusionMultiplierStream.ExtrusionRatio);
Assert.AreEqual(targetExtrusionRate, printer.Connection.ExtrusionMultiplierStream.ExtrusionRatio, $"Unexpected Extrusion Rate - {scope}");
testRunner.WaitFor(() => targetFeedRate == printer.Connection.FeedRateMultiplierStream.FeedRateRatio);
Assert.AreEqual(targetFeedRate, printer.Connection.FeedRateMultiplierStream.FeedRateRatio, $"Unexpected Feed Rate - {scope}");
}
}
}

View file

@ -0,0 +1,39 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("MatterControl.AutomationTests")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("MatterHackers, Inc.")]
[assembly: AssemblyProduct("MatterControl.AutomationTests")]
[assembly: AssemblyCopyright("Copyright © 2015")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("418e7058-92ee-4329-86ba-ac26b65afb25")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
// https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1416
[assembly: System.Runtime.Versioning.SupportedOSPlatform("windows7.0")]

View file

@ -0,0 +1,8 @@
{
"profiles": {
"MatterControl.AutomationTests": {
"commandName": "Project",
"nativeDebugging": true
}
}
}

View file

@ -0,0 +1,160 @@
/*
Copyright (c) 2014, Lars Brubaker
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the FreeBSD Project.
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MatterHackers.Agg.UI;
using MatterHackers.GuiAutomation;
using MatterHackers.MatterControl.PartPreviewWindow;
using MatterHackers.MatterControl.SlicerConfiguration;
using MatterHackers.MatterControl.VersionManagement;
using NUnit.Framework;
using TestInvoker;
namespace MatterHackers.MatterControl.Tests.Automation
{
[TestFixture, Category("MatterControl.UI.Automation")]
public class ReSliceTests
{
// [Test, Category("Emulator"), Ignore("WIP")]
[Test, ChildProcessTest, Category("Emulator")]
public async Task ReSliceHasCorrectEPositions()
{
await MatterControlUtilities.RunTest((testRunner) =>
{
// testRunner.ClickByName("Connection Wizard Skip Sign In Button");
using (var emulator = testRunner.LaunchAndConnectToPrinterEmulator())
{
ApplicationController.Instance.Allow32BitReSlice = true;
var printer = testRunner.FirstPrinter();
printer.Settings.SetValue(SettingsKey.enable_line_splitting, "0");
var view3D = testRunner.GetWidgetByName("View3DWidget", out _) as View3DWidget;
var scene = view3D.Object3DControlLayer.Scene;
// Add a callback to check that every line has an extruder
// distance greater than the largest distance minus the max retraction
// amount and less than some amount that is reasonable
double lastAbsoluteEPosition = 0;
double largestAbsoluteEPosition = 0;
double largestRetraction = 0;
emulator.EPositionChanged += (e, s) =>
{
largestAbsoluteEPosition = Math.Max(largestAbsoluteEPosition, emulator.CurrentExtruder.AbsoluteEPosition);
var delta = emulator.CurrentExtruder.AbsoluteEPosition - lastAbsoluteEPosition;
if (delta < largestRetraction)
{
largestRetraction = delta;
}
double printerRetraction = 7 + .1; // the airwolf has a retraction of 7 mm
Assert.GreaterOrEqual(delta, -printerRetraction, "We should never move back more than the retraction amount");
Assert.GreaterOrEqual(emulator.CurrentExtruder.AbsoluteEPosition, largestAbsoluteEPosition - printerRetraction, "Never go back more than the retraction amount");
Assert.LessOrEqual(emulator.CurrentExtruder.AbsoluteEPosition, lastAbsoluteEPosition + 10, "We should never move up more than 10 mm");
lastAbsoluteEPosition = emulator.CurrentExtruder.AbsoluteEPosition;
};
// Add a cube to the bed
testRunner.NavigateToFolder("Queue Row Item Collection")
.ClickByName("Row Item cube_20x20x20.stl")
.ClickByName("Print Library Overflow Menu")
.ClickByName("Add to Bed Menu Item")
.Delay()
.ClickByName("Print Library Overflow Menu")
.Delay()
.ClickByName("Add to Bed Menu Item")
.Delay()
// start the print
.StartPrint(printer, pauseAtLayers: "50;60")
// Wait for pause
// the yes button is 'Resume'
// NOTE: ClickByName Failed: Named GuiWidget not found [No Button]
// It appears that printing can just take too long.
// This might be fixed by using a semaphore in MatterHackers.PrinterEmulator.Emulator.
.ClickByName("No Button", secondsToWait: 80)
// Delete the cube
.ClickByName("Bed Options Menu")
.ClickByName("Clear Bed Menu Item")
.Delay();
// ensure there is nothing on the bed
Assert.AreEqual(0, scene.Children.Count);
// Add a cylinder
testRunner.NavigateToFolder("Queue Row Item Collection")
.ClickByName("Row Item cylinder_5x20.stl")
.ClickByName("Print Library Overflow Menu")
.ClickByName("Add to Bed Menu Item")
.ClickByName("Add Content Menu")
// re-slice the part
.ClickByName("Re-Slice Button")
// The change to new g-code
.ClickByName("Switch Button", secondsToWait: 10)
// and resume the print
.ClickByName("Resume Task Button")
// Wait for next pause
.ClickByName("No Button", secondsToWait: 80)
// Switch back to the cube
// Delete the cylinder
.ClickByName("Bed Options Menu")
.ClickByName("Clear Bed Menu Item");
// ensure there is nothing on the bed
Assert.AreEqual(0, scene.Children.Count);
// add the cube
testRunner.NavigateToFolder("Queue Row Item Collection")
.ClickByName("Row Item cube_20x20x20.stl")
.ClickByName("Print Library Overflow Menu")
.ClickByName("Add to Bed Menu Item")
.ClickByName("Add Content Menu")
// re-slice the part
.ClickByName("Re-Slice Button")
.ClickByName("Switch Button", secondsToWait: 10)
// and resume the print
.ClickByName("Resume Task Button")
// Wait for done
.WaitForPrintFinished(printer);
// this will make sure we turned off line splitting and had good data about the extruder position
Assert.AreEqual(-7, largestRetraction, "Airwolf HD has a retraction of 7mm, make sure we had one");
}
return Task.CompletedTask;
},
maxTimeToRun: 390,
queueItemFolderToAdd: QueueTemplate.ReSliceParts);
}
}
}

View file

@ -0,0 +1,581 @@
/*
Copyright (c) 2022, Lars Brubaker, John Lewin
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the FreeBSD Project.
*/
using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MatterHackers.Agg;
using MatterHackers.Agg.Platform;
using MatterHackers.Agg.UI;
using MatterHackers.DataConverters3D;
using MatterHackers.GuiAutomation;
using MatterHackers.MatterControl.PartPreviewWindow;
using MatterHackers.VectorMath;
using Newtonsoft.Json;
using NUnit.Framework;
using TestInvoker;
namespace MatterHackers.MatterControl.Tests.Automation
{
[TestFixture, Category("MatterControl.UI.Automation"), Parallelizable(ParallelScope.Children)]
public class SceneUndoRedoCopyTests
{
private const string CoinName = "MatterControl - Coin.stl";
[SetUp]
public void TestSetup()
{
StaticData.RootPath = MatterControlUtilities.StaticDataPath;
MatterControlUtilities.OverrideAppDataLocation(MatterControlUtilities.RootPath);
}
[Test, ChildProcessTest]
public async Task CopyRemoveUndoRedo()
{
await MatterControlUtilities.RunTest(testRunner =>
{
testRunner.OpenPartTab();
var view3D = testRunner.GetWidgetByName("View3DWidget", out _) as View3DWidget;
var scene = view3D.Object3DControlLayer.Scene;
// Initialize
testRunner.AddItemToBed();
testRunner.Select3DPart("Calibration - Box.stl")
.WaitForName("Duplicate Button");
Assert.AreEqual(1, scene.Children.Count, "Should have 1 part before copy");
for (int i = 0; i <= 4; i++)
{
testRunner.ClickByName("Duplicate Button");
testRunner.WaitFor(() => scene.Children.Count == i + 2);
Assert.AreEqual(i + 2, scene.Children.Count);
}
testRunner.ClickByName("Remove Button");
testRunner.WaitFor(() => scene.Children.Count == 5);
Assert.AreEqual(5, scene.Children.Count, "Should have 5 parts after Remove");
testRunner.ClickByName("3D View Undo");
testRunner.WaitFor(() => scene.Children.Count == 6);
Assert.AreEqual(6, scene.Children.Count, "Should have 6 parts after Undo");
testRunner.ClickByName("3D View Redo");
testRunner.WaitFor(() => scene.Children.Count == 5);
Assert.AreEqual(5, scene.Children.Count, "Should have 5 parts after Redo");
return Task.CompletedTask;
}, overrideWidth: 1300);
}
[Test, ChildProcessTest]
public async Task UndoRedoCopy()
{
await MatterControlUtilities.RunTest(testRunner =>
{
testRunner.OpenPartTab()
.AddItemToBed();
var view3D = testRunner.GetWidgetByName("View3DWidget", out _) as View3DWidget;
var scene = view3D.Object3DControlLayer.Scene;
testRunner.Select3DPart("Calibration - Box.stl");
Assert.AreEqual(1, scene.Children.Count, "There should be 1 part on the bed after AddDefaultFileToBedplate()");
// Add 5 items
for (int i = 0; i <= 4; i++)
{
testRunner.ClickByName("Duplicate Button");
testRunner.Delay(.5);
}
Assert.AreEqual(6, scene.Children.Count, "There should be 6 parts on the bed after the copy loop");
// Perform and validate 5 undos
for (int x = 0; x <= 4; x++)
{
int meshCountBeforeUndo = scene.Children.Count;
testRunner.ClickByName("3D View Undo");
testRunner.WaitFor(() => scene.Children.Count == meshCountBeforeUndo - 1);
Assert.AreEqual(scene.Children.Count, meshCountBeforeUndo - 1);
}
testRunner.Delay(.2);
// Perform and validate 5 redoes
for (int z = 0; z <= 4; z++)
{
int meshCountBeforeRedo = scene.Children.Count;
testRunner.ClickByName("3D View Redo");
testRunner.WaitFor(() => meshCountBeforeRedo + 1 == scene.Children.Count);
Assert.AreEqual(meshCountBeforeRedo + 1, scene.Children.Count);
}
return Task.CompletedTask;
}, overrideWidth: 1300);
}
[Test, ChildProcessTest]
public async Task ValidateDoUndoOnUnGroupSingleMesh()
{
await MatterControlUtilities.RunTest(testRunner =>
{
testRunner.OpenPartTab();
var view3D = testRunner.GetWidgetByName("View3DWidget", out _) as View3DWidget;
var scene = view3D.Object3DControlLayer.Scene;
// Initialize
testRunner.AddItemToBed(partName: "Row Item MH Logo.stl")
.Delay(.1)
.ClickByName("MH Logo.stl");
Assert.IsNotNull(scene.SelectedItem);
testRunner.WaitFor(() => scene.Children.Count() == 1);
Assert.AreEqual(1, scene.Children.Count());
// test un-group single mesh
testRunner.RunDoUndoTest(
scene,
() =>
{
// Set focus
testRunner.ClickByName("View3DWidget")
// Ungroup
.SelectAll()
.ClickByName("Ungroup Button")
// Blur
.SelectNone()
// Assert
.WaitFor(() => scene.Children.Count() == 3);
Assert.AreEqual(3, scene.Children.Count());
});
return Task.CompletedTask;
}, overrideWidth: 1300);
}
[Test, ChildProcessTest]
public async Task ValidateDoUndoOnGroup2Items()
{
await MatterControlUtilities.RunTest(testRunner =>
{
testRunner.OpenPartTab();
var view3D = testRunner.GetWidgetByName("View3DWidget", out _) as View3DWidget;
var scene = view3D.Object3DControlLayer.Scene;
// Initialize
AddBoxABoxBToBed(testRunner, scene);
Assert.AreEqual(2, scene.Children.Count());
// test group 2 objects
testRunner.RunDoUndoTest(
scene,
() =>
{
// Select all
testRunner.ClickByName("View3DWidget")
.SelectAll()
// Group items
.ClickByName("Group Button")
// Clear selection
.SelectNone()
// Assert
.WaitFor(() => scene.SelectedItem == null)
.WaitFor(() => scene.Children.Count() == 1);
Assert.AreEqual(1, scene.Children.Count());
});
return Task.CompletedTask;
}, overrideWidth: 1300);
}
[Test, ChildProcessTest]
public async Task ValidateDoUndoUnGroup2Items()
{
await MatterControlUtilities.RunTest(testRunner =>
{
testRunner.OpenPartTab();
var view3D = testRunner.GetWidgetByName("View3DWidget", out _) as View3DWidget;
var scene = view3D.Object3DControlLayer.Scene;
// Initialize
AddBoxABoxBToBed(testRunner, scene);
Assert.AreEqual(2, scene.Children.Count());
Assert.AreEqual(3, scene.DescendantsAndSelf().Count(), "The scene and the 2 objects");
// Select all
testRunner.ClickByName("View3DWidget")
.SelectAll()
// Group
.ClickByName("Group Button")
// Blur
.SelectNone()
.WaitFor(() => scene.Children.Count() == 1);
Assert.AreEqual(1, scene.Children.Count());
// group object can now process holes so it is a source object and has an extra object in it.
Assert.AreEqual(5, scene.DescendantsAndSelf().Count(), "The scene, the group and the 2 objects");
// test un-group 2 grouped objects
testRunner.RunDoUndoTest(
scene,
() =>
{
// Ungroup
testRunner.SelectAll();
testRunner.ClickByName("Ungroup Button");
// Blur
testRunner.SelectNone();
testRunner.WaitFor(() => scene.Children.Count() == 2);
// Assert
Assert.AreEqual(2, scene.Children.Count());
Assert.AreEqual(3, scene.DescendantsAndSelf().Count(), "The scene and the 2 objects");
});
return Task.CompletedTask;
}, overrideWidth: 1300);
}
[Test, ChildProcessTest]
public async Task ValidateDoUndoMirror()
{
await MatterControlUtilities.RunTest(testRunner =>
{
testRunner.OpenPartTab();
var view3D = testRunner.GetWidgetByName("View3DWidget", out _) as View3DWidget;
var scene = view3D.Object3DControlLayer.Scene;
// Initialize
AddCoinToBed(testRunner, scene);
// test mirror operations
testRunner.RunDoUndoTest(
scene,
() =>
{
// Mirror
testRunner.RightClickByName(CoinName, offset: new Point2D(-5, 0))
.ClickByName("Modify Menu Item")
.ClickByName("Transform Menu Item")
.ClickByName("Mirror Menu Item")
.ClickByName("Mirror On DropDownList");
});
return Task.CompletedTask;
}, overrideWidth: 1300);
}
// NOTE: This test once failed on GLFW. Could be timing or accidental input.
[Test, ChildProcessTest]
public async Task ValidateDoUndoTranslateXY()
{
await MatterControlUtilities.RunTest(testRunner =>
{
testRunner.OpenPartTab();
var view3D = testRunner.GetWidgetByName("View3DWidget", out _) as View3DWidget;
var scene = view3D.Object3DControlLayer.Scene;
// Initialize
AddCoinToBed(testRunner, scene);
// NOTE: Test failed with this once:
// Should be same (6): ' "Matrix": "[1.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,1.0,0.0,-10.000661239027977,-19.05065578967333,-5.421010862427522E-17,1.0]",
// ' ' "Matrix": "[1.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,1.0,0.0,2.999338760972023,-24.00067986547947,-5.421010862427522E-17,1.0]",
// Expected: True
// But was: False
// UndoTestExtensionMethods.SceneFilesAreSame(String fileName1, String fileName2, Boolean expectedResult)line474
// UndoTestExtensionMethods.AssertUndoRedo(AutomationRunner testRunner, InteractiveScene scene, String scenePath, String preOperationPath, String postOperationPath, Int32 preOperationDescendantCount, Int32 postOperationDescendantCount) line 541
// UndoTestExtensionMethods.RunDoUndoTest(AutomationRunner testRunner, InteractiveScene scene, Action performOperation) line 453
// test drag x y translation
testRunner.RunDoUndoTest(
scene,
() =>
{
// Drag in XY
var part = testRunner.GetObjectByName(CoinName, out _) as IObject3D;
var start = part.GetAxisAlignedBoundingBox(Matrix4X4.Identity).Center;
testRunner.DragDropByName(CoinName, CoinName, offsetDrag: new Point2D(-4, 0), offsetDrop: new Point2D(40, 0));
var end = part.GetAxisAlignedBoundingBox(Matrix4X4.Identity).Center;
// NOTE: Test failed with this once: Expected: greater than 15.399987526237965d, But was: 15.399987526237965d
// ClickWidget now waits for 2 redraws in case there is more deferred processing.
// Assert
Assert.Greater(end.X, start.X);
Assert.Less(end.Y, start.Y);
Assert.True(Math.Abs(end.Z - start.Z) < .001);
});
return Task.CompletedTask;
}, overrideWidth: 1300);
}
// Parallel testing of this single test.
//[Test, ChildProcessTest] public async Task ValidateDoUndoTranslateXY1() => await ValidateDoUndoTranslateXY();
//[Test, ChildProcessTest] public async Task ValidateDoUndoTranslateXY2() => await ValidateDoUndoTranslateXY();
//[Test, ChildProcessTest] public async Task ValidateDoUndoTranslateXY3() => await ValidateDoUndoTranslateXY();
//[Test, ChildProcessTest] public async Task ValidateDoUndoTranslateXY4() => await ValidateDoUndoTranslateXY();
//[Test, ChildProcessTest] public async Task ValidateDoUndoTranslateXY5() => await ValidateDoUndoTranslateXY();
//[Test, ChildProcessTest] public async Task ValidateDoUndoTranslateXY6() => await ValidateDoUndoTranslateXY();
//[Test, ChildProcessTest] public async Task ValidateDoUndoTranslateXY7() => await ValidateDoUndoTranslateXY();
//[Test, ChildProcessTest] public async Task ValidateDoUndoTranslateXY8() => await ValidateDoUndoTranslateXY();
//[Test, ChildProcessTest] public async Task ValidateDoUndoTranslateXY9() => await ValidateDoUndoTranslateXY();
//[Test, ChildProcessTest] public async Task ValidateDoUndoTranslateXYa() => await ValidateDoUndoTranslateXY();
//[Test, ChildProcessTest] public async Task ValidateDoUndoTranslateXYb() => await ValidateDoUndoTranslateXY();
[Test, ChildProcessTest]
public async Task ValidateDoUndoTranslateZ()
{
await MatterControlUtilities.RunTest(testRunner =>
{
testRunner.OpenPartTab();
var view3D = testRunner.GetWidgetByName("View3DWidget", out _) as View3DWidget;
var scene = view3D.Object3DControlLayer.Scene;
// Initialize
AddCoinToBed(testRunner, scene);
// test z translation
testRunner.RunDoUndoTest(
scene,
() =>
{
// Drag in Z
var part = testRunner.GetObjectByName(CoinName, out _) as IObject3D;
var startZ = part.GetAxisAlignedBoundingBox(Matrix4X4.Identity).Center.Z;
testRunner.DragDropByName("MoveInZControl", "MoveInZControl", offsetDrag: new Point2D(0, 0), offsetDrop: new Point2D(0, 40))
.Delay();
var endZ = part.GetAxisAlignedBoundingBox(Matrix4X4.Identity).Center.Z;
// Assert
Assert.Greater(endZ, startZ);
});
return Task.CompletedTask;
}, overrideWidth: 1300);
}
private static void AddBoxABoxBToBed(AutomationRunner testRunner, InteractiveScene scene)
{
var item = "Calibration - Box.stl";
// NOTE: Test once failed here. Probably due to timing.
testRunner.AddItemToBed()
.Delay(.1)
// move the first one over
.DragDropByName(item, item, offsetDrop: new Point2D(40, 40));
var part = testRunner.GetObjectByName(item, out _) as IObject3D;
part.Name = "BoxA";
testRunner.AddItemToBed()
.Delay(.1);
part = testRunner.GetObjectByName(item, out _) as IObject3D;
part.Name = "BoxB";
}
private static void AddCoinToBed(AutomationRunner testRunner, InteractiveScene scene)
{
testRunner.AddItemToBed(partName: "Row Item MatterControl - Coin.stl")
.Delay(.1)
// TODO: assert the part is centered on the bed
.ClickByName(CoinName, offset: new Point2D(-4, 0));
Assert.IsNotNull(scene.SelectedItem);
}
}
public static class UndoTestExtensionMethods
{
public static void RunDoUndoTest(this AutomationRunner testRunner, InteractiveScene scene, Action performOperation)
{
string scenePath = Path.Combine(MatterControlUtilities.RootPath, "Tests", "temp", "undo_test_scene_" + Path.GetRandomFileName());
Directory.CreateDirectory(scenePath);
Object3D.AssetsPath = Path.Combine(scenePath, "Assets");
Object3D.AssetsPath = Path.Combine(scenePath, "Assets");
// save the scene
string preOperationPath = Path.Combine(scenePath, "preOperation.mcx");
scene.Save(preOperationPath);
var preOperationDescendantCount = scene.DescendantsAndSelf().Count();
// Do the operation
performOperation();
var postOperationDescendantCount = scene.DescendantsAndSelf().Count();
// save the scene
string postOperationPath = Path.Combine(scenePath, scenePath, "postOperation.mcx");
scene.Save(postOperationPath);
Assert.AreEqual(postOperationDescendantCount, scene.DescendantsAndSelf().Count());
// assert new save is different
SceneFilesAreSame(postOperationPath, preOperationPath, false);
// select the part
testRunner.Type("^a"); // clear the selection (type a space)
testRunner.WaitFor(() => scene.SelectedItem != null);
Assert.IsNotNull(scene.SelectedItem);
// with the part selected
AssertUndoRedo(
testRunner,
scene,
scenePath,
preOperationPath,
postOperationPath,
preOperationDescendantCount,
postOperationDescendantCount);
// unselect the part
testRunner.Type(" ") // clear the selection (type a space)
.WaitFor(() => scene.SelectedItem == null);
Assert.IsNull(scene.SelectedItem);
// with the part unselected
AssertUndoRedo(
testRunner,
scene,
scenePath,
preOperationPath,
postOperationPath,
preOperationDescendantCount,
postOperationDescendantCount);
}
private static void SceneFilesAreSame(string fileName1, string fileName2, bool expectedResult)
{
bool areSame = true;
string[] fileContent1 = File.ReadAllLines(fileName1);
string[] fileContent2 = File.ReadAllLines(fileName2);
for (int i = 0; i < Math.Min(fileContent1.Length, fileContent2.Length); i++)
{
areSame &= ValidateSceneLine(fileContent1[i], fileContent2[i]);
if (expectedResult)
{
Assert.IsTrue(areSame, $"Should be same ({i}): '{fileContent1[i]}' '{fileContent2[i]}");
}
}
areSame &= fileContent1.Length == fileContent2.Length;
if (expectedResult)
{
Assert.IsTrue(areSame, $"Should be same length: '{fileName1}' '{fileName2}");
}
Assert.IsTrue(expectedResult == areSame, $"Should be different: '{fileName1}' '{fileName2}");
}
private static bool ValidateSceneLine(string v1, string v2)
{
if (v1 == v2)
{
return true;
}
if (v1.Contains("Matrix")
&& v2.Contains("Matrix"))
{
double[] test = new double[] { 0, 1, 2, 3 };
var expected = JsonConvert.SerializeObject(test, Formatting.Indented);
// Figure out if the value content of these lines are equivalent.
var data1 = v1.Substring(v1.IndexOf('['), v1.IndexOf(']') - v1.IndexOf('[') + 1);
var matrix1 = new Matrix4X4(JsonConvert.DeserializeObject<double[]>(data1));
var data2 = v2.Substring(v2.IndexOf('['), v2.IndexOf(']') - v2.IndexOf('[') + 1);
var matrix2 = new Matrix4X4(JsonConvert.DeserializeObject<double[]>(data2));
if (matrix1.Equals(matrix2, .001))
{
return true;
}
}
return false;
}
private static void AssertUndoRedo(AutomationRunner testRunner,
InteractiveScene scene,
string scenePath, string preOperationPath, string postOperationPath,
int preOperationDescendantCount, int postOperationDescendantCount)
{
var preUndoDescendantsCount = scene.DescendantsAndSelf().Count();
// do an undo
testRunner.ClickByName("3D View Undo");
testRunner.WaitFor(() => preOperationDescendantCount == scene.DescendantsAndSelf().Count());
Assert.AreEqual(preOperationDescendantCount, scene.DescendantsAndSelf().Count());
// save the undo data
string undoScenePath = Path.Combine(scenePath, "undoScene.mcx");
var totalSceneItems = scene.DescendantsAndSelf().Count();
var selectedItem = scene.SelectedItem;
Object3D.AssetsPath = Path.Combine(scenePath, "Assets");
scene.Save(undoScenePath);
Assert.AreEqual(totalSceneItems, scene.DescendantsAndSelf().Count());
Assert.AreEqual(selectedItem, scene.SelectedItem);
// After undo action, validate the persisted undoScene with the original 'before do' scene
SceneFilesAreSame(preOperationPath, undoScenePath, true);
// now redo the undo
testRunner.ClickByName("3D View Redo");
testRunner.WaitFor(() => postOperationDescendantCount == scene.DescendantsAndSelf().Count());
Assert.AreEqual(postOperationDescendantCount, scene.DescendantsAndSelf().Count());
// save the redo
string redoScenePath = Path.Combine(scenePath, "redoScene.mcx");
scene.Save(redoScenePath);
// After redo action, validate the persisted redoScene with the original 'after do' scene
SceneFilesAreSame(postOperationPath, redoScenePath, true);
}
}
}

View file

@ -0,0 +1,780 @@
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;
using TestInvoker;
namespace MatterHackers.MatterControl.Tests.Automation
{
[TestFixture, Category("MatterControl.UI.Automation"), Parallelizable(ParallelScope.Children)]
public class SliceSetingsTests
{
[Test, ChildProcessTest]
public async Task RaftEnabledPassedToSliceEngine()
{
await MatterControlUtilities.RunTest((testRunner) =>
{
testRunner.WaitForFirstDraw()
.AddAndSelectPrinter()
.AddTestAssetsToLibrary(new[] { "Rook.amf" })
.AddItemToBed("", "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, ChildProcessTest]
public async Task RelativeRetractionExecutesCorrectly()
{
// NOTE: This test once timed out at 120, but took 38.4s when run on its own.
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.AddItemToBed()
.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, ChildProcessTest, 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.AddItemToBed()
.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, ChildProcessTest, 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, ChildProcessTest, 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, ChildProcessTest, 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, ChildProcessTest, 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.AddItemToBed()
.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 */, ChildProcessTest]
public async Task ClearingCheckBoxClearsUserOverride()
{
await MatterControlUtilities.RunTest((testRunner) =>
{
testRunner.WaitForFirstDraw()
.AddAndSelectPrinter("Airwolf 3D", "HD")
// Navigate to Local Library
.SwitchToPrinterSettings()
.ClickByName("Features SliceSettingsTab");
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, ChildProcessTest]
public async Task DualExtrusionShowsCorrectHotendData()
{
await MatterControlUtilities.RunTest((testRunner) =>
{
using (var emulator = testRunner.LaunchAndConnectToPrinterEmulator())
{
testRunner.ClickByName("Features SliceSettingsTab");
// 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)
ThemedNumberEdit tempWidget = testRunner.GetWidgetByName("Temperature Input", out _) as ThemedNumberEdit;
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
// NOTE: Got test failure twice: The printer should report the expected goal temp
// Expected: 220
// But was: 60
// Even though WaitFor was used. Maybe the emulator is just delayed sometimes.
// Adding Math.Round anyway. And more waiting.
testRunner.ClickByName("Restore temperature")
.WaitFor(() => hipsGoalTemp == (int)Math.Round(emulator.CurrentExtruder.TargetTemperature), maxSeconds: 10);
Assert.AreEqual(hipsGoalTemp, (int)Math.Round(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 SliceSettingsTab")
.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, ChildProcessTest]
public void SliceSettingsOrganizerSupportsKeyLookup()
{
StaticData.RootPath = MatterControlUtilities.StaticDataPath;
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, ChildProcessTest]
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, ChildProcessTest]
public async Task ValidateSaveMenuItemLabels()
{
await MatterControlUtilities.RunTest((testRunner) =>
{
testRunner.AddAndSelectPrinter("Airwolf 3D", "HD");
// Navigate to Slice Settings Tab and make sure Layer Thickness row is visible
testRunner.SwitchToSliceSettings()
.NavigateToSliceSettingsField(SettingsKey.layer_height);
// Set Quality to "Coarse" and Material to "ABS"
testRunner.ClickByName("Quality")
.ClickByName("Coarse Menu")
.ClickByName("Material")
.ClickByName("ABS Menu")
.RightClickByName("Layer Thickness OverrideIndicator", offset: new Point2D(30, 0))
.ClickByName("Save to Menu Item")
.Delay(.5);
Assert.IsTrue(testRunner.NameExists("Quality Setting 'Coarse' Menu Item"));
Assert.IsTrue(testRunner.NameExists("Material Setting 'ABS' Menu Item"));
// Set Quality to "Fine" and Material to "BENDLAY"
testRunner.ClickByName("Quality")
.ClickByName("Fine Menu")
.ClickByName("Material")
.ClickByName("BENDLAY Menu")
.RightClickByName("Layer Thickness OverrideIndicator", offset: new Point2D(30, 0))
.ClickByName("Save to Menu Item")
.Delay(.5);
Assert.IsTrue(testRunner.NameExists("Quality Setting 'Fine' Menu Item"));
Assert.IsTrue(testRunner.NameExists("Material Setting 'BENDLAY' Menu Item"));
// Set Quality to none
testRunner.ClickByName("Quality")
.ClickByName("- none - Menu Item")
.RightClickByName("Layer Thickness OverrideIndicator", offset: new Point2D(30, 0))
.ClickByName("Save to Menu Item")
.Delay(.5);
Assert.IsTrue(testRunner.NameExists("Quality Setting Menu Item"));
Assert.IsTrue(testRunner.NameExists("Material Setting 'BENDLAY' Menu Item"));
// Set Quality to "Standard" and Material to none
testRunner.ClickByName("Quality")
.ClickByName("Standard Menu")
.ClickByName("Material")
.ClickByName("- none - Menu Item")
.RightClickByName("Layer Thickness OverrideIndicator", offset: new Point2D(30, 0))
.ClickByName("Save to Menu Item")
.Delay(.5);
Assert.IsTrue(testRunner.NameExists("Quality Setting 'Standard' Menu Item"));
Assert.IsTrue(testRunner.NameExists("Material Setting Menu Item"));
return Task.CompletedTask;
}, maxTimeToRun: 1000);
}
[Test, ChildProcessTest]
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<bool>(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<bool>(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<bool>(settingToChange) == expected);
Assert.IsFalse(printer.Settings.UserLayer.ContainsKey(settingToChange));
}
[Test, ChildProcessTest]
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()
.ClickByName("Slice Settings Overflow Menu")
.ClickByName("Advanced Menu Item")
.Delay()
.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) // NOTE: Happened once: System.Exception : ClickByName Failed: Named GuiWidget not found [Hardware SliceSettingsTab]
//.Delay(1.0) // Wait for reload reliably:
.WaitForReloadAll(() => testRunner.SelectSliceSettingsField(SettingsKey.has_heated_bed))
.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, ChildProcessTest]
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<double>(SettingsKey.layer_height) == 0.5);
Assert.AreEqual(printer.Settings.GetValue<double>(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<double>(SettingsKey.layer_height) == 0.1);
Assert.AreEqual(printer.Settings.GetValue<double>(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<double>(SettingsKey.layer_height) == 0.1);
Assert.AreEqual(printer.Settings.GetValue<double>(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<double>(SettingsKey.layer_height) == 0.5);
Assert.AreEqual(printer.Settings.GetValue<double>(SettingsKey.layer_height).ToString(), "0.5", "Layer height is what we set it to");
return Task.CompletedTask;
}, maxTimeToRun: 120);
}
[Test, ChildProcessTest]
public void CopyFromTest()
{
StaticData.RootPath = MatterControlUtilities.StaticDataPath;
MatterControlUtilities.OverrideAppDataLocation(MatterControlUtilities.RootPath);
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<ImageWidget>().First();
closeWidget.InvokeClick();
}
}
}
}
}

View file

@ -0,0 +1,71 @@
using MatterHackers.Agg;
using MatterHackers.Agg.Image;
using MatterHackers.PolygonMesh;
using MatterHackers.Agg.UI;
using NUnit.Framework;
using System;
using System.Linq;
using System.Threading.Tasks;
using MatterHackers.GuiAutomation;
using MatterHackers.Agg.PlatformAbstract;
using MatterHackers.MatterControl.PartPreviewWindow;
using System.IO;
using MatterHackers.MatterControl.CreatorPlugins;
using MatterHackers.Agg.UI.Tests;
using MatterHackers.MatterControl.PrintQueue;
using MatterHackers.MatterControl.DataStorage;
using System.Diagnostics;
namespace MatterHackers.MatterControl.UI
{
[TestFixture, Category("MatterControl.UI"), RunInApplicationDomain]
public class SlicingTests
{
[Test, RequiresSTA, RunInApplicationDomain]
public void Slicing()
{
// Run a copy of MatterControl
Action<AutomationTesterHarness> testToRun = (AutomationTesterHarness resultsHarness) =>
{
AutomationRunner testRunner = new AutomationRunner(MatterControlUtilities.DefaultTestImages);
{
//Navigate to Local Library
MatterControlUtilities.SelectAndAddPrinter(testRunner, "Airwolf 3D", "HD", true);
testRunner.ClickByName("Library Tab");
MatterControlUtilities.NavigateToFolder(testRunner, "Local Library Row Item Collection");
testRunner.Wait(1);
testRunner.ClickByName("Row Item Calibration - Box");
testRunner.ClickByName("Row Item Calibration - Box Print Button");
testRunner.Wait(1);
testRunner.ClickByName("Layer View Tab");
testRunner.Wait(1);
testRunner.ClickByName("SettingsAndControls");
testRunner.Wait(1);
testRunner.ClickByName("Settings Tab");
testRunner.Wait(1.1);
testRunner.ClickByName("Skirt and Raft Tab");
testRunner.Wait(1);
testRunner.ClickByName("Create Raft Checkbox");
testRunner.Wait(1);
testRunner.ClickByName("Generate Gcode Button");
testRunner.Wait(1.5);
MatterControlUtilities.CloseMatterControl(testRunner);
}
};
AutomationTesterHarness testHarness = MatterControlUtilities.RunTest(testToRun);
Assert.IsTrue(testHarness.AllTestsPassed);
Assert.IsTrue(testHarness.TestCount == 3); // make sure we ran all our tests
}
}
}

View file

@ -0,0 +1,44 @@
using System.Threading;
using System.Threading.Tasks;
using MatterHackers.MatterControl.PartPreviewWindow;
using NUnit.Framework;
using TestInvoker;
namespace MatterHackers.MatterControl.Tests.Automation
{
[TestFixture, Category("MatterControl.UI.Automation")]
public class SqLiteLibraryProviderTests
{
[Test, ChildProcessTest]
public async Task LibraryQueueViewRefreshesOnAddItem()
{
await MatterControlUtilities.RunTest((testRunner) =>
{
testRunner.OpenPartTab()
.AddItemToBed();
var view3D = testRunner.GetWidgetByName("View3DWidget", out _) as View3DWidget;
var scene = view3D.Object3DControlLayer.Scene;
testRunner.WaitFor(() => scene.SelectedItem != null);
Assert.IsNotNull(scene.SelectedItem, "Expect part selection after Add to Bed action");
testRunner.ClickByName("Duplicate Button")
// wait for the copy to finish
.Delay(.1)
.ClickByName("Remove Button")
.SaveBedplateToFolder("0Test Part", "Local Library Row Item Collection")
// Click Home -> Local Library
.NavigateToLibraryHome()
.NavigateToFolder("Local Library Row Item Collection");
// ensure that it is now in the library folder (that the folder updated)
Assert.IsTrue(testRunner.WaitForName("Row Item 0Test Part"), "The part we added should be in the library");
testRunner.Delay(.5);
return Task.CompletedTask;
}, queueItemFolderToAdd: QueueTemplate.Three_Queue_Items, overrideWidth: 1300);
}
}
}