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);
}
}
}

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>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
<RuntimeIdentifier>win</RuntimeIdentifier>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<!-- AppVeyor builder wants the old paths. -->
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
</PropertyGroup>
<ItemGroup>
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Community.CsharpSqlite\Community.CsharpSqlite.csproj" />
<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\Localizations\Localizations.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\MatterSlice.csproj" />
<ProjectReference Include="..\..\Submodules\MatterSlice\MatterSliceLib\MatterSliceLib.csproj" />
</ItemGroup>
<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 Remove="MatterControl\LibraryProviderSqliteTests.cs" />
<Compile Remove="MatterControl\LibraryProviderTests.cs" />
<Compile Remove="MatterControl\Slicing\SliceMappingTests.cs" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,95 @@
/*
Copyright (c) 2018, 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.Tasks;
using MatterHackers.Agg;
using MatterHackers.Agg.Platform;
using MatterHackers.MatterControl;
using MatterHackers.MatterControl.Tests.Automation;
using Newtonsoft.Json;
using NUnit.Framework;
namespace MatterControl.Tests.MatterControl
{
[TestFixture]
public class ApplicationControllerTests
{
[Test]
public async Task LoadCachableShouldFallbackToStaticData()
{
StaticData.RootPath = MatterControlUtilities.StaticDataPath;
MatterControlUtilities.OverrideAppDataLocation(MatterControlUtilities.RootPath);
string cacheScope = Path.Combine("Some", "Specific", "Scope");
// Load cacheable content from StaticData when the collector returns null and no cached content exists
var versionInfo = await ApplicationController.LoadCacheableAsync<VersionInfo>(
cacheKey: "HelloWorld-File1",
cacheScope: cacheScope,
collector: async () =>
{
// Hypothetical http request with 304 indicating our results cached results have not expired
return null;
},
staticDataFallbackPath: "BuildInfo.txt");
Assert.IsNotNull(versionInfo, "LoadCacheable should fall back to StaticData content if collection fails");
string cachePath = ApplicationController.CacheablePath(cacheScope, "HelloWorld-File1");
Assert.IsFalse(File.Exists(cachePath), "After fall back to StaticData content, cache should not contain fall back content");
}
[Test]
public async Task LoadCachableShouldStoreCollectedResults()
{
StaticData.RootPath = MatterControlUtilities.StaticDataPath;
MatterControlUtilities.OverrideAppDataLocation(MatterControlUtilities.RootPath);
string cacheScope = Path.Combine("Some", "Specific", "Scope");
// Load cacheable content from collector, writing results to cache
var versionInfo = await ApplicationController.LoadCacheableAsync<VersionInfo>(
cacheKey: "HelloWorld-File1",
cacheScope: cacheScope,
collector: async () =>
{
return JsonConvert.DeserializeObject<VersionInfo>("{\"BuildVersion\": \"HelloFromCollector\"}");
},
staticDataFallbackPath: "BuildInfo.txt");
Assert.IsTrue(versionInfo.BuildVersion == "HelloFromCollector", "LoadCacheable should use content from collector");
string cachePath = ApplicationController.CacheablePath(cacheScope, "HelloWorld-File1");
Assert.IsTrue(File.Exists(cachePath), "Collected results should be written to cache at expected path");
Assert.IsTrue(File.ReadAllText(cachePath).Contains("HelloFromCollector"), "Cached content should equal collected content");
}
}
}

View file

@ -0,0 +1,149 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using MatterHackers.Agg;
using MatterHackers.Agg.Platform;
using MatterHackers.DataConverters3D;
using MatterHackers.MatterControl.DataStorage;
using MatterHackers.MatterControl.Tests.Automation;
using MatterHackers.PolygonMesh;
using NUnit.Framework;
namespace MatterControl.Tests.MatterControl
{
[TestFixture]
public class AssetManagerTests
{
[Test]
public async Task StoreAssetFile()
{
StaticData.RootPath = MatterControlUtilities.StaticDataPath;
MatterControlUtilities.OverrideAppDataLocation(MatterControlUtilities.RootPath);
// Create sample asset file
string tempFile = ApplicationDataStorage.Instance.GetNewLibraryFilePath(".txt");
Directory.CreateDirectory(Path.GetDirectoryName(tempFile));
File.WriteAllText(tempFile, "Sample Text");
// Set directory for asset resolution
Object3D.AssetsPath = ApplicationDataStorage.Instance.LibraryAssetsPath;
Directory.CreateDirectory(Object3D.AssetsPath);
foreach (var file in Directory.GetFiles(Object3D.AssetsPath))
{
File.Delete(file);
}
Assert.AreEqual(0, Directory.GetFiles(Object3D.AssetsPath).Length);
// Create AssetManager
var assetManager = new MockAssetManager();
AssetObject3D.AssetManager = assetManager;
// Store
string result = await AssetObject3D.AssetManager.StoreFile(tempFile, false, CancellationToken.None, null);
// Validate
Assert.AreEqual(1, Directory.GetFiles(Object3D.AssetsPath).Length, "Unexpected asset file count");
Assert.AreEqual("8FB7B108E5F0A7FAE84DF849DDE830FED5B5F786.txt", result, "Unexpected asset name");
Assert.AreEqual(0, assetManager.PublishCount, "No files should have been published");
}
class AssetTestObject3D : AssetObject3D
{
public override string AssetPath { get; set ; }
}
[Test]
public async Task StoreAsset()
{
StaticData.RootPath = MatterControlUtilities.StaticDataPath;
MatterControlUtilities.OverrideAppDataLocation(MatterControlUtilities.RootPath);
// Create sample asset file
string tempFile = ApplicationDataStorage.Instance.GetNewLibraryFilePath(".txt");
Directory.CreateDirectory(Path.GetDirectoryName(tempFile));
File.WriteAllText(tempFile, "Sample Text");
var object3D = new AssetTestObject3D()
{
AssetPath = tempFile
};
// Set directory for asset resolution
Object3D.AssetsPath = ApplicationDataStorage.Instance.LibraryAssetsPath;
Directory.CreateDirectory(Object3D.AssetsPath);
foreach(var file in Directory.GetFiles(Object3D.AssetsPath))
{
File.Delete(file);
}
Assert.AreEqual(0, Directory.GetFiles(Object3D.AssetsPath).Length);
// Create AssetManager
var assetManager = new MockAssetManager();
AssetObject3D.AssetManager = assetManager;
// Store
await AssetObject3D.AssetManager.StoreAsset(object3D, false, CancellationToken.None, null);
// Validate
Assert.AreEqual(1, Directory.GetFiles(Object3D.AssetsPath).Length, "Unexpected asset file count");
Assert.AreEqual("8FB7B108E5F0A7FAE84DF849DDE830FED5B5F786", object3D.AssetID, "Unexpected AssetID");
Assert.AreEqual("8FB7B108E5F0A7FAE84DF849DDE830FED5B5F786.txt", object3D.AssetPath, "Unexpected asset name");
Assert.AreEqual(0, assetManager.PublishCount, "No files should have been published");
}
[Test]
public async Task StoreMesh()
{
StaticData.RootPath = MatterControlUtilities.StaticDataPath;
MatterControlUtilities.OverrideAppDataLocation(MatterControlUtilities.RootPath);
// Create sample asset file
string tempFile = ApplicationDataStorage.Instance.GetNewLibraryFilePath(".txt");
Directory.CreateDirectory(Path.GetDirectoryName(tempFile));
File.WriteAllText(tempFile, "Sample Text");
var object3D = new Object3D()
{
Mesh = PlatonicSolids.CreateCube(1, 1, 1)
};
// Set directory for asset resolution
Object3D.AssetsPath = ApplicationDataStorage.Instance.LibraryAssetsPath;
Directory.CreateDirectory(Object3D.AssetsPath);
foreach (var file in Directory.GetFiles(Object3D.AssetsPath))
{
File.Delete(file);
}
Assert.AreEqual(0, Directory.GetFiles(Object3D.AssetsPath).Length);
// Create AssetManager
var assetManager = new MockAssetManager();
AssetObject3D.AssetManager = assetManager;
// Store
await AssetObject3D.AssetManager.StoreMesh(object3D, false, CancellationToken.None, null);
// Validate
Assert.AreEqual(1, Directory.GetFiles(Object3D.AssetsPath).Length, "Unexpected asset file count");
Assert.AreEqual("CF58E6637ED36311082F66EC5FA3A279E0513FE6.stl", object3D.MeshPath, "Unexpected MeshPath");
Assert.AreEqual(0, assetManager.PublishCount, "No files should have been published");
}
// Create AssetManager
public class MockAssetManager : AssetManager
{
public int PublishCount { get; private set; } = 0;
public override Task PublishAsset(string assetID, CancellationToken cancellationToken, Action<double, string> progress)
{
this.PublishCount++;
return Task.CompletedTask;
}
}
}
}

View file

@ -0,0 +1,54 @@
using System.Collections.Generic;
using System.Linq;
using MatterHackers.Agg;
using MatterHackers.Agg.Platform;
using MatterHackers.Agg.UI;
using MatterHackers.MatterControl;
using MatterHackers.MatterControl.SettingsManagement;
using MatterHackers.MatterControl.Tests.Automation;
using NUnit.Framework;
namespace MatterControl.Tests.MatterControl
{
class BoundDropListTests
{
[Test]
public void BoundDropListHonorsWhitelist()
{
StaticData.RootPath = MatterControlUtilities.StaticDataPath;
MatterControlUtilities.OverrideAppDataLocation(MatterControlUtilities.RootPath);
var manufacturers = new string[] { "3D Factory", "3D Stuffmaker", "Airwolf 3D", "BCN3D", "BeeVeryCreative", "Blue Eagle Labs",
"Deezmaker", "FlashForge", "gCreate", "IRA3D", "JumpStart", "Leapfrog", "Lulzbot", "MAKEiT", "Maker's Tool Works",
"MakerBot", "MakerGear", "Me3D", "OpenBeam", "Organic Thinking System", "Other", "Portabee", "Printrbot", "PrintSpace",
"Revolution 3D Printers", "ROBO 3D", "SeeMeCNC", "Solidoodle", "Tosingraf", "Type A Machines", "Ultimaker", "Velleman",
"Wanhao" };
var allManufacturers = manufacturers.Select(m => new KeyValuePair<string, string>(m, m)).ToList();
BoundDropList dropList;
var theme = new ThemeConfig();
// Whitelist on non-OEM builds should contain all printers
dropList = new BoundDropList("Test", theme);
dropList.ListSource = allManufacturers;
Assert.Greater(dropList.MenuItems.Count, 20);
var whitelist = new List<string> { "3D Stuffmaker" };
OemSettings.Instance.SetManufacturers(allManufacturers, whitelist);
dropList = new BoundDropList("Test", theme);
dropList.ListSource = OemSettings.Instance.AllOems;
Assert.AreEqual(1, dropList.MenuItems.Count);
whitelist.Add("Airwolf 3D");
OemSettings.Instance.SetManufacturers(allManufacturers, whitelist);
dropList = new BoundDropList("Test", theme);
dropList.ListSource = OemSettings.Instance.AllOems;
Assert.AreEqual(2, dropList.MenuItems.Count);
}
}
}

View file

@ -0,0 +1,92 @@
/*
Copyright (c) 2018, 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 MatterHackers.MatterControl.Plugins.BrailleBuilder;
using NUnit.Framework;
namespace MatterControl.Tests.MatterControl
{
[TestFixture, Category("TextCreator")]
public class BrailleGrade2Tests
{
[Test]
public static void ConvertBrailleText()
{
Assert.IsTrue(BrailleGrade2.ConvertWord("taylor") == "taylor");
Assert.IsTrue(BrailleGrade2.ConvertWord("Taylor") == ",taylor");
Assert.IsTrue(BrailleGrade2.ConvertWord("TayLor") == ",tay,lor");
Assert.IsTrue(BrailleGrade2.ConvertWord("energy") == "5}gy");
Assert.IsTrue(BrailleGrade2.ConvertWord("men") == "m5");
Assert.IsTrue(BrailleGrade2.ConvertWord("runabout") == "runab");
Assert.IsTrue(BrailleGrade2.ConvertWord("afternoon") == "afn");
Assert.IsTrue(BrailleGrade2.ConvertWord("really") == "re,y");
Assert.IsTrue(BrailleGrade2.ConvertWord("glance") == "gl.e");
Assert.IsTrue(BrailleGrade2.ConvertWord("station") == "/,n");
Assert.IsTrue(BrailleGrade2.ConvertWord("as") == "z");
Assert.IsTrue(BrailleGrade2.ConvertWord("abby") == "a2y");
Assert.IsTrue(BrailleGrade2.ConvertWord("commitment") == "-mit;t");
Assert.IsTrue(BrailleGrade2.ConvertWord("mother") == "\"m");
Assert.IsTrue(BrailleGrade2.ConvertWord("myself") == "myf");
Assert.IsTrue(BrailleGrade2.ConvertWord("lochness") == "lo*;s");
Assert.IsTrue(BrailleGrade2.ConvertWord("Seven o'clock") == ",sev5 o'c");
Assert.IsTrue(BrailleGrade2.ConvertWord("test") == "te/");
Assert.IsTrue(BrailleGrade2.ConvertWord("that") == "t");
Assert.IsTrue(BrailleGrade2.ConvertWord("will") == "w");
Assert.IsTrue(BrailleGrade2.ConvertWord("show") == "%{");
Assert.IsTrue(BrailleGrade2.ConvertWord("our") == "|r");
Assert.IsTrue(BrailleGrade2.ConvertWord("with") == ")");
Assert.IsTrue(BrailleGrade2.ConvertWord("braille") == "brl");
Assert.IsTrue(BrailleGrade2.ConvertWord("conformance") == "3=m.e");
Assert.IsTrue(BrailleGrade2.ConvertString("go to sleep") == "g 6sleep");
Assert.IsTrue(BrailleGrade2.ConvertString("go to") == "g to");
Assert.IsTrue(BrailleGrade2.ConvertString("here it is") == "\"h x is");
Assert.IsTrue(BrailleGrade2.ConvertString("test that will show our conformance with braille") == "te/ t w %{ |r 3=m.e ) brl");
Assert.IsTrue(BrailleGrade2.ConvertString("so we can create some strings and then this gives us the output that is expected") == "s we c cr1te \"s /r+s & !n ? gives u ! |tput t is expect$");
Assert.IsTrue(BrailleGrade2.ConvertString("Waltz, bad nymph, for quick jigs vex.") == ",waltz1 bad nymph1 = qk jigs vex4");
Assert.IsTrue(BrailleGrade2.ConvertString("Quick zephyrs blow, vexing daft Jim.") == ",qk zephyrs bl{1 vex+ daft ,jim4");
Assert.IsTrue(BrailleGrade2.ConvertString("Sphinx of black quartz, judge my vow.") == ",sph9x ( black qu>tz1 judge my v{4");
Assert.IsTrue(BrailleGrade2.ConvertString("Two driven jocks help fax my big quiz.") == ",two driv5 jocks help fax my big quiz4");
// Assert.IsTrue(BrailleGrade2.ConvertString("Five quacking zephyrs jolt my wax bed.") == ",five quack+ zephyrs jolt my wax b$4");
Assert.IsTrue(BrailleGrade2.ConvertString("The five boxing wizards jump quickly.") == ",! five box+ wiz>ds jump qkly4");
Assert.IsTrue(BrailleGrade2.ConvertString("Pack my box with five dozen liquor jugs.") == ",pack my box ) five doz5 liquor jugs4");
Assert.IsTrue(BrailleGrade2.ConvertString("The quick brown fox jumps over the lazy dog.") == ",! qk br{n fox jumps ov} ! lazy dog4");
Assert.IsTrue(BrailleGrade2.ConvertString("Jinxed wizards pluck ivy from the big quilt.") == ",j9x$ wiz>ds pluck ivy f ! big quilt4");
Assert.IsTrue(BrailleGrade2.ConvertString("Crazy Fredrick bought many very exquisite opal jewels.") == ",crazy ,fr$rick b\"| _m v exquisite opal jewels4");
Assert.IsTrue(BrailleGrade2.ConvertString("We promptly judged antique ivory buckles for the next prize.") == ",we promptly judg$ antique ivory buckles =! next prize4");
Assert.IsTrue(BrailleGrade2.ConvertString("A mad boxer shot a quick, gloved jab to the jaw of his dizzy opponent.") == ",a mad box} %ot a qk1 glov$ jab 6! jaw ( 8 dizzy opp\"ont4");
Assert.IsTrue(BrailleGrade2.ConvertString("Jaded zombies acted quaintly but kept driving their oxen forward.") == ",jad$ zombies act$ qua9tly b kept driv+ _! ox5 =w>d4");
Assert.IsTrue(BrailleGrade2.ConvertString("14. The job requires extra pluck and zeal from every young wage earner.") == "#ad4 ,! job requires extra pluck & z1l f e \"y wage e>n}4");
Assert.IsTrue(BrailleGrade2.ConvertString("Just wanting to put together some more tests to show the effectiveness of our converter.") == ",j want+ 6put tgr \"s m te/s 6%{ ! e6ective;s ( |r 3v}t}4");
}
}
}

View file

@ -0,0 +1,122 @@
/*
Copyright (c) 2016, 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 MatterHackers.Agg;
using MatterHackers.Agg.Platform;
using MatterHackers.MatterControl;
using MatterHackers.MatterControl.PrinterCommunication;
using MatterHackers.MatterControl.SlicerConfiguration;
using MatterHackers.MatterControl.Tests.Automation;
using NUnit.Framework;
namespace MatterControl.Tests.MatterControl
{
[TestFixture]
public class GCodeProcessingTests
{
[Test, Category("GCodeProcessing")]
public void ReadTemperaturesCorrectly()
{
ParseTempAndValidate("ok B:12.0 /0.0 T0:12.8 /0.0 T1:12.8 /0.0 T2:12.8 /0.0 @:0 B@:0", 12.8, 12.8, 12.8, 12.0);
ParseTempAndValidate("ok T:139.6 /0.0 @:0.00W", 139.6, 0, 0, 0.0);
ParseTempAndValidate("ok T:139.6 B:136.2 /0.0 @:0.00W", 139.6, 0, 0, 136.2);
}
private void ParseTempAndValidate(string gcodeString, double? extruder0, double? extruder1, double? extruder2, double? bedTemp)
{
double[] extruders = new double[16];
double bed = 0;
PrinterConnection.ParseTemperatureString(gcodeString, extruders, null, ref bed, null);
Assert.IsTrue(extruders[0] == extruder0);
Assert.IsTrue(extruders[1] == extruder1);
Assert.IsTrue(extruders[2] == extruder2);
Assert.IsTrue(bed == bedTemp);
}
[Test]
public void SmoothieDualExtruderM105Response()
{
double[] extruders = new double[16];
double bed = 0;
// As of 2018-11-29 with an unknown users config
string smoothieDualExtruderM105Response = "ok T:220.1 /220.0 @109 T1:222.7 /221.0 @49 B:32.2 /0.0 @0";
PrinterConnection.ParseTemperatureString(smoothieDualExtruderM105Response, extruders, null, ref bed, null);
Assert.AreEqual("220.1", extruders[0].ToString("0.#"), "Incorrect Extruder 0 result");
Assert.AreEqual("222.7", extruders[1].ToString("0.#"), "Incorrect Extruder 1 result");
}
[Test, Category("GCodeProcessing")]
public void ReplaceMacroValuesWorking()
{
StaticData.RootPath = MatterControlUtilities.StaticDataPath;
MatterControlUtilities.OverrideAppDataLocation(MatterControlUtilities.RootPath);
var settings = new PrinterSettings();
settings.Slicer = new EngineMappingsMatterSlice();
void TestMacroReplacement(string inputText, string outputControl)
{
Assert.AreEqual(outputControl, settings.ReplaceSettingsNamesWithValues(inputText));
}
TestMacroReplacement("[temperature]", "200");
TestMacroReplacement("[first_layer_speed]", "1080");
TestMacroReplacement("[bed_remove_part_temperature]", "0");
TestMacroReplacement("[bridge_fan_speed]", "100");
TestMacroReplacement("[bridge_speed]", "3");
TestMacroReplacement("[external_perimeter_speed]", "1260");
TestMacroReplacement("[extruder_wipe_temperature]", "0");
TestMacroReplacement("[filament_diameter]", "3");
TestMacroReplacement("[first_layer_bed_temperature]", "70");
TestMacroReplacement("[first_layer_temperature]", "205");
TestMacroReplacement("{max_fan_speed}", "100");
TestMacroReplacement("{min_fan_speed}", "35");
TestMacroReplacement("{perimeter_speed}", "1800");
TestMacroReplacement("{raft_print_speed}", "3600");
TestMacroReplacement("{retract_length}", "1");
TestMacroReplacement("{retract_speed}", "1800");
TestMacroReplacement("{support_material_speed}", "3600");
TestMacroReplacement("{temperature}", "200");
TestMacroReplacement("[bed_temperature]", "70");
TestMacroReplacement("{infill_speed}", "3600");
TestMacroReplacement("{min_print_speed}", "600");
TestMacroReplacement("{travel_speed}", "7800");
// make sure we pick up the right temp when there is a bed surface change
settings.SetValue(SettingsKey.has_swappable_bed, "1");
settings.SetValue(SettingsKey.bed_temperature_blue_tape, "84.2");
settings.SetValue(SettingsKey.bed_surface, "Blue Tape");
TestMacroReplacement("[bed_temperature]", "84.2");
}
}
}

View file

@ -0,0 +1,913 @@
/*
Copyright (c) 2016, Kevin Pope, 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.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using MatterControl.Printing;
using MatterHackers.Agg.Platform;
using MatterHackers.MatterControl;
using MatterHackers.MatterControl.Library.Export;
using MatterHackers.MatterControl.PrinterCommunication.Io;
using MatterHackers.MatterControl.SlicerConfiguration;
using MatterHackers.MatterControl.Tests.Automation;
using MatterHackers.VectorMath;
using Newtonsoft.Json;
using NUnit.Framework;
using TestInvoker;
namespace MatterControl.Tests.MatterControl
{
[TestFixture, Category("GCodeStream"), Parallelizable(ParallelScope.Children)]
public class GCodeStreamTests
{
[SetUp]
public void TestSetup()
{
StaticData.RootPath = MatterControlUtilities.StaticDataPath;
MatterControlUtilities.OverrideAppDataLocation(MatterControlUtilities.RootPath);
}
[Test, ChildProcessTest]
public void MaxLengthStreamTests()
{
string[] lines = new string[]
{
"G1 X0 Y0 Z0 E0 F500",
"M105",
"G1 X18 Y0 Z0 F2500",
"G28",
"G1 X0 Y0 Z0 E0 F500",
};
// We should go back to the above code when possible. It requires making pause part and move while paused part of the stream.
// All communication should go through stream to minimize the difference between printing and controlling while not printing (all printing in essence).
string[] expected = new string[]
{
"G1 X0 Y0 Z0 E0 F500",
"M105",
"G1 X6 F2500",
"G1 X12",
"G1 X18",
"G28",
"G1 X0 Y0 Z0 E0 F500",
};
PrinterConfig printer = null;
MaxLengthStream maxLengthStream = new MaxLengthStream(printer, new TestGCodeStream(printer, lines), 6, true);
ValidateStreamResponse(expected, maxLengthStream);
}
[Test, ChildProcessTest]
public void ExportStreamG30Tests()
{
string[] inputLines = new string[]
{
"M117 Starting Print",
"M104 S0",
"; comment line",
"G28 ; home all axes",
"G0 Z10 F1800",
"G0 Z11 F1800",
"G0 X1Y0Z9 F1800",
"G0 Z10 F1801",
"G30 Z0",
"M114",
"G0 Z10 F1800",
"M114",
"M109 S[temperature]",
};
// We should go back to the above code when possible. It requires making pause part and move while paused part of the stream.
// All communication should go through stream to minimize the difference between printing and controlling while not printing (all printing in essence).
string[] expected = new string[]
{
"M117 Starting Print",
"M104 S0",
"; comment line",
"G28 ; home all axes",
"G1 Z10 F1800",
"G1 Z11",
"G1 X1 Y0 Z9",
"G1 Z10 F1801",
"G30 Z0",
"M114",
"G1 Z10 F1800",
"M114",
"M109 S[temperature]",
};
var printer = new PrinterConfig(new PrinterSettings());
var testStream = GCodeExport.GetExportStream(printer, new TestGCodeStream(printer, inputLines), true);
ValidateStreamResponse(expected, testStream);
}
[Test, ChildProcessTest]
public void SmoothieRewriteTest()
{
string[] inputLines = new string[]
{
"G28",
"M119",
};
// We should go back to the above code when possible. It requires making pause part and move while paused part of the stream.
// All communication should go through stream to minimize the difference between printing and controlling while not printing (all printing in essence).
string[] expected = new string[]
{
"G28",
"M280 P0 S10.6",
"G4 P400",
"M280 P0 S7",
"G4 P400",
"M117 Ready ",
"M119",
"switch filament; WRITE_RAW",
};
var printer = new PrinterConfig(new PrinterSettings());
var write_filter = "\"^(G28)\", \"G28,M280 P0 S10.6,G4 P400,M280 P0 S7,G4 P400,M117 Ready \"";
write_filter += "\\n\"^(M119)\", \"M119,switch filament; WRITE_RAW\"";
printer.Settings.SetValue(SettingsKey.write_regex, write_filter);
var testStream = GCodeExport.GetExportStream(printer, new TestGCodeStream(printer, inputLines), true);
ValidateStreamResponse(expected, testStream);
}
[Test, ChildProcessTest]
public void LineCuttingOffWhenNoLevelingTest()
{
string[] inputLines = new string[]
{
"G1 X0Y0Z0E0 F1000",
"G1 X10 Y0 Z0 F1000",
};
// We should go back to the above code when possible. It requires making pause part and move while paused part of the stream.
// All communication should go through stream to minimize the difference between printing and controlling while not printing (all printing in essence).
string[] expected = new string[]
{
"G1 X0 Y0 Z0 E0 F1000",
"G1 X10",
};
var printer = new PrinterConfig(new PrinterSettings());
printer.Settings.SetValue(SettingsKey.has_hardware_leveling, "1");
var testStream = GCodeExport.GetExportStream(printer, new TestGCodeStream(printer, inputLines), true);
ValidateStreamResponse(expected, testStream);
}
[Test, ChildProcessTest]
public void LineCuttingOnWhenLevelingOnWithProbeTest()
{
string[] inputLines = new string[]
{
"G1 X0Y0Z0E0F1000",
"G1 X0Y0Z0E1F1000",
"G1 X10 Y0 Z0 F1000",
};
string[] expected = new string[]
{
PrintLevelingStream.SoftwareLevelingAppliedMessage,
"G1 X0 Y0 Z-0.1 E0 F1000",
"G1 E1",
"G1 X1",
"G1 X2",
"G1 X3",
"G1 X4",
"G1 X5",
"G1 X6",
"G1 X7",
"G1 X8",
"G1 X9",
"G1 X10",
};
var printer = new PrinterConfig(new PrinterSettings());
var levelingData = new PrintLevelingData()
{
SampledPositions = new List<Vector3>()
{
new Vector3(0, 0, 0),
new Vector3(10, 0, 0),
new Vector3(5, 10, 0)
}
};
printer.Settings.SetValue(SettingsKey.print_leveling_data, JsonConvert.SerializeObject(levelingData));
printer.Settings.SetValue(SettingsKey.has_z_probe, "1");
printer.Settings.SetValue(SettingsKey.use_z_probe, "1");
printer.Settings.SetValue(SettingsKey.probe_offset, "0,0,-.1");
printer.Settings.SetValue(SettingsKey.print_leveling_enabled, "1");
var testStream = GCodeExport.GetExportStream(printer, new TestGCodeStream(printer, inputLines), true);
ValidateStreamResponse(expected, testStream);
}
[Test, ChildProcessTest]
public void LineCuttingOnWhenLevelingOnNoProbeTest()
{
string[] inputLines = new string[]
{
"G1 X0Y0Z0E0F1000",
"G1 X0Y0Z0E1F1000",
"G1 X10 Y0 Z0 F1000",
};
string[] expected = new string[]
{
PrintLevelingStream.SoftwareLevelingAppliedMessage,
"G1 X0 Y0 Z-0.1 E0 F1000",
"G1 E1",
"G1 X1",
"G1 X2",
"G1 X3",
"G1 X4",
"G1 X5",
"G1 X6",
"G1 X7",
"G1 X8",
"G1 X9",
"G1 X10",
};
var printer = new PrinterConfig(new PrinterSettings());
var levelingData = new PrintLevelingData()
{
SampledPositions = new List<Vector3>()
{
new Vector3(0, 0, -.1),
new Vector3(10, 0, -.1),
new Vector3(5, 10, -.1)
}
};
printer.Settings.SetValue(SettingsKey.print_leveling_data, JsonConvert.SerializeObject(levelingData));
printer.Settings.SetValue(SettingsKey.probe_offset, "0,0,-.1");
printer.Settings.SetValue(SettingsKey.print_leveling_enabled, "1");
var testStream = GCodeExport.GetExportStream(printer, new TestGCodeStream(printer, inputLines), true);
ValidateStreamResponse(expected, testStream);
}
public static GCodeStream CreateTestGCodeStream(PrinterConfig printer, string[] inputLines, out List<GCodeStream> streamList)
{
streamList = new List<GCodeStream>();
streamList.Add(new TestGCodeStream(printer, inputLines));
streamList.Add(new PauseHandlingStream(printer, streamList[streamList.Count - 1]));
streamList.Add(new QueuedCommandsStream(printer, streamList[streamList.Count - 1]));
streamList.Add(new RelativeToAbsoluteStream(printer, streamList[streamList.Count - 1]));
streamList.Add(new WaitForTempStream(printer, streamList[streamList.Count - 1]));
streamList.Add(new BabyStepsStream(printer, streamList[streamList.Count - 1]));
streamList.Add(new MaxLengthStream(printer, streamList[streamList.Count - 1], 1, true));
streamList.Add(new ExtrusionMultiplierStream(printer, streamList[streamList.Count - 1]));
streamList.Add(new FeedRateMultiplierStream(printer, streamList[streamList.Count - 1]));
GCodeStream totalGCodeStream = streamList[streamList.Count - 1];
return totalGCodeStream;
}
[Test, ChildProcessTest]
public void RegexReplacementStreamIsLast()
{
var printer = new PrinterConfig(new PrinterSettings());
var context = GCodeExport.GetExportStream(printer, new TestGCodeStream(printer, new[] { "" }), true);
var streamProcessors = new List<GCodeStream>();
while (context is GCodeStream gCodeStream)
{
streamProcessors.Add(context);
context = gCodeStream.InternalStream;
}
Assert.IsTrue(streamProcessors.First() is ProcessWriteRegexStream, "ProcessWriteRegexStream should be the last stream in the stack");
}
[Test, ChildProcessTest]
public void CorrectEOutputPositionsG91()
{
string[] inputLines = new string[]
{
"G1 E11 F300", // E = 11
// Before:
"G92 E0", // E = 0
"G91",
"G1 E - 5 F302", // E = -5
"G90",
// After:
"G91",
"G1 E8 F150", // E = 3
"G90",
"G4 P0",
"G92 E0", // E = 0
"G4 P0",
"G91",
"G1 E-2 F301", // E = -2
"G90",
};
// We should go back to the above code when possible. It requires making pause part and move while paused part of the stream.
// All communication should go through stream to minimize the difference between printing and controlling while not printing (all printing in essence).
string[] expected = new string[]
{
"G1 E11 F300",
"G92 E0",
"",
"G1 E-1 F302",
"G1 E-2",
"G1 E-3",
"G1 E-4",
"G1 E-5",
"G90",
"", // 10
"G1 E-4 F150",
"G1 E-3",
"G1 E-2",
"G1 E-1",
"G1 E0",
"G1 E1",
"G1 E2",
"G1 E3",
"",
"G4 P0",
"G92 E0",
"G4 P0",
"",
"G1 E-1 F301",
"G1 E-2",
"",
};
var printer = new PrinterConfig(new PrinterSettings());
GCodeStream testStream = CreateTestGCodeStream(printer, inputLines, out List<GCodeStream> streamList);
ValidateStreamResponse(expected, testStream);
}
[Test, ChildProcessTest]
public void CorrectEOutputPositionsM83()
{
string[] inputLines = new string[]
{
"G1 E11 F300",
// Before:
"G92 E0",
"M83", // relative extruder
"G1 E - 5 F302",
"M82",
// After:
"M83",
"G1 E8 F150",
"M82",
"G4 P0",
"G92 E0",
"G4 P0",
"M83",
"G1 E-2 F301",
"M82",
"G1 E2 F301",
};
string[] expected = new string[]
{
"G1 E11 F300",
"G92 E0",
"",
"G1 E-1 F302",
"G1 E-2",
"G1 E-3",
"G1 E-4",
"G1 E-5",
"M82",
"", // 10
"G1 E-4 F150",
"G1 E-3",
"G1 E-2",
"G1 E-1",
"G1 E0",
"G1 E1",
"G1 E2",
"G1 E3",
"",
"G4 P0", // 20
"G92 E0",
"G4 P0",
"",
"G1 E-1 F301",
"G1 E-2",
"",
"G1 E-1",
"G1 E0",
"G1 E1",
"G1 E2", // 30
};
var printer = new PrinterConfig(new PrinterSettings());
GCodeStream testStream = CreateTestGCodeStream(printer, inputLines, out List<GCodeStream> streamList);
ValidateStreamResponse(expected, testStream);
}
[Test, ChildProcessTest]
public void CorrectEOutputForMiniStartupWithM83()
{
string[] inputLines = new string[]
{
"G21 ; set units to millimeters",
"M107 ; fan off",
"T0 ; set the active extruder to 0",
"; settings from start_gcode",
"M83",
"M104 S170 ; set hotend temperature for bed leveling",
"M140 S60 ; set bed temperature",
"M109 R170",
"G28",
"G29",
"M104 S215 ; set hotend temperature",
"G92 E0.0",
"G1 X0 Y0 F2400",
"G1 Z3 F720",
"G92 E0.0",
"G1 X5 F1000",
"G1 Z0 F720",
"G1 X10 E5 F900",
"G1 X15 E5",
"G92 E0.0",
"; automatic settings after start_gcode",
"T0 ; set the active extruder to 0",
"G90 ; use absolute coordinates",
"G92 E0 ; reset the expected extruder position",
"M82 ; use absolute distance for extrusion",
"G1 E5 F440",
"G1 E10",
};
string[] expected = new string[]
{
"G21 ; set units to millimeters",
"M107 ; fan off",
"T0 ; set the active extruder to 0",
"; settings from start_gcode",
"", // set to relative e
"M104 S170 ; set hotend temperature for bed leveling",
"M140 S60 ; set bed temperature",
"M109 R170",
"G28",
"G29", // 10
"M104 S215 ; set hotend temperature",
"G92 E0.0",
"G1 F2400",
"G1 Z1 F720",
"G1 Z2",
"G1 Z3",
"G92 E0.0",
"G1 X1 F1000",
"G1 X2",
"G1 X3", // 20
"G1 X4",
"G1 X5",
"G1 F720",
"G1 X6 E1 F900",
"G1 X7 E2",
"G1 X8 E3",
"G1 X9 E4",
"G1 X10 E5",
"G1 X11 E6",
"G1 X12 E7", // 30
"G1 X13 E8",
"G1 X14 E9",
"G1 X15 E10",
"G92 E0.0",
"; automatic settings after start_gcode",
"T0 ; set the active extruder to 0",
"G90 ; use absolute coordinates",
"G92 E0 ; reset the expected extruder position",
"M82 ; use absolute distance for extrusion",
"G1 E1 F440", // 40
"G1 E2",
"G1 E3",
"G1 E4",
"G1 E5",
"G1 E6",
"G1 E7",
"G1 E8",
"G1 E9",
"G1 E10",
};
var printer = new PrinterConfig(new PrinterSettings());
GCodeStream testStream = CreateTestGCodeStream(printer, inputLines, out List<GCodeStream> streamList);
ValidateStreamResponse(expected, testStream);
}
[Test, ChildProcessTest]
public void CorrectZOutputPositions()
{
string[] inputLines = new string[]
{
"G1 Z-2 F300",
"G92 Z0",
"G1 Z5 F300",
"G28",
};
// We should go back to the above code when possible. It requires making pause part and move while paused part of the stream.
// All communication should go through stream to minimize the difference between printing and controlling while not printing (all printing in essence).
string[] expected = new string[]
{
"G1 Z-2 F300",
"G92 Z0",
"G1 Z1 F300",
"G1 Z2",
"G1 Z3",
"G1 Z4",
"G1 Z5",
"G28",
};
var printer = new PrinterConfig(new PrinterSettings());
GCodeStream testStream = CreateTestGCodeStream(printer, inputLines, out List<GCodeStream> streamList);
ValidateStreamResponse(expected, testStream);
}
[Test, ChildProcessTest]
public void PauseHandlingStreamTests()
{
int readX = 50;
// Validate that the number parsing code is working as expected, specifically ignoring data that appears in comments
// This is a regression that we saw in the Lulzbot Mini profile after adding macro processing.
GCodeFile.GetFirstNumberAfter("X", "G1 Z10 E - 10 F12000 ; suck up XXmm of filament", ref readX);
// did not change
Assert.AreEqual(50, readX, "Don't change the x if it is after a comment");
// a comments that looks more like a valid line
GCodeFile.GetFirstNumberAfter("X", "G1 Z10 E - 10 F12000 ; X33", ref readX);
// did not change
Assert.AreEqual(50, readX, "Don't change the x if it is after a comment");
// a line that should parse
GCodeFile.GetFirstNumberAfter("X", "G1 Z10 E - 10 F12000 X33", ref readX);
// did change
Assert.AreEqual(33, readX, "not in a comment, do a change");
string[] inputLines = new string[]
{
"; the printer is moving normally",
"G1 X10 Y10 Z10 E0",
"G1 X10 Y10 Z10 E10",
"G1 X10 Y10 Z10 E30",
"; the printer pauses",
"G91",
"G1 Z10 E - 10 F12000 ; suck up XXmm of filament",
"G90",
"; the user moves the printer",
"; the printer un-pauses",
"G91",
"G1 Z-10 E10.8 F12000",
"G90",
};
// We should go back to the above code when possible. It requires making pause part and move while paused part of the stream.
// All communication should go through stream to minimize the difference between printing and controlling while not printing (all printing in essence).
string[] expected = new string[]
{
"; the printer is moving normally",
"G1 X10 Y10 Z10 E0",
"G1 E10",
"G1 E30",
"; the printer pauses",
"", // G91 is removed
"G1 Z20 E20 F12000", // altered to be absolute
"G90",
"; the user moves the printer",
"; the printer un-pauses",
"", // G91 is removed
"G1 Z10 E30.8",
"", // G90 is removed
};
var printer = new PrinterConfig(new PrinterSettings());
GCodeStream pauseHandlingStream = CreateTestGCodeStream(printer, inputLines, out List<GCodeStream> streamList);
ValidateStreamResponse(expected, pauseHandlingStream);
}
[Test, ChildProcessTest, Ignore("WIP")]
public void SoftwareEndstopstreamTests()
{
string[] inputLines = new string[]
{
// test x min
// move without extrusion
"G1 X100Y100Z0E0", // start at the bed center
"G1 X-100", // move left off the bed
"G1 Y110", // move while outside bounds
"G1 X100", // move back on
// move with extrusion
"G1 X100Y100Z0E0", // start at the bed center
"G1 X-100E10", // move left off the bed
"G1 Y110E20", // move while outside bounds
"G1 X100E30", // move back on
// test x max
// test y min
// test y max
// test z min
// test z max
};
// We should go back to the above code when possible. It requires making pause part and move while paused part of the stream.
// All communication should go through stream to minimize the difference between printing and controlling while not printing (all printing in essence).
string[] expected = new string[]
{
// move without extrusion
"G1 X100 Y100 Z0 E0", // start position
"G1 X0", // clamped x
"", // move while outside
"G1 Y110", // first position back in bounds
"G1 X100", // move to requested x
// move with extrusion
"G1 X100Y100Z0E0", // start at the bed center
"G1 X-100E10", // move left off the bed
"G1 Y110E20", // move while outside bounds
"G1 X100E30", // move back on
};
var printer = new PrinterConfig(new PrinterSettings());
var pauseHandlingStream = new SoftwareEndstopsStream(printer, new TestGCodeStream(printer, inputLines));
ValidateStreamResponse(expected, pauseHandlingStream);
}
[Test, ChildProcessTest]
public void MorePauseHandlingStreamTests()
{
string[] inputLines = new string[]
{
"; the printer is moving normally",
"G1 X10 Y10 Z10 E0",
"G1 X11 Y10 Z10 E10",
"G1 X12 Y10 Z10 E30",
"; the printer pauses",
"@pause",
"; do_resume", // just a marker for us to issue a resume
// move some more
"G1 X13 Y10 Z10 E40",
};
// We should go back to the above code when possible. It requires making pause part and move while paused part of the stream.
// All communication should go through stream to minimize the difference between printing and controlling while not printing (all printing in essence).
string[] expected = new string[]
{
"; the printer is moving normally",
"G1 X10 Y10 Z10 E0",
"G1 X11 E10",
"G1 X12 E30",
"; the printer pauses",
"",
"",
"G1 Z20 E20 F12000",
"G90",
"M114",
"",
"; do_resume",
"G92 E-10",
"G1 Z16.67 F3001",
"G1 X12.01 Y10.01 Z13.34",
"G1 Z10.01",
"G1 X12 Y10 Z10 F3000",
"",
"G1 Z0 E30.8 F12000",
"", // G90 removed
"M114",
"",
"G1 X12.1 F1800",
"G1 X12.2",
"", // G90 removed
"G1 X12.33 Z1.667 E32.333",
"G1 X12.47 Z3.333 E33.867",
"G1 X12.6 Z5 E35.4",
"G1 X12.73 Z6.667 E36.933",
"G1 X12.87 Z8.333 E38.467",
"G1 X13 Z10 E40",
};
// this is the pause and resume from the Eris
var printer = new PrinterConfig(new PrinterSettings());
printer.Settings.SetValue(SettingsKey.pause_gcode, "G91\nG1 Z10 E - 10 F12000\n G90");
printer.Settings.SetValue(SettingsKey.resume_gcode, "G91\nG1 Z-10 E10.8 F12000\nG90");
GCodeStream pauseHandlingStream = CreateTestGCodeStream(printer, inputLines, out List<GCodeStream> streamList);
ValidateStreamResponse(expected, pauseHandlingStream, streamList);
}
public static void ValidateStreamResponse(string[] expected, GCodeStream testStream, List<GCodeStream> streamList = null)
{
int lineIndex = 0;
// Advance
string actualLine = testStream.ReadLine();
string expectedLine = expected[lineIndex++];
while (actualLine != null)
{
if (actualLine.StartsWith("G92 E0"))
{
testStream.SetPrinterPosition(new PrinterMove(default(Vector3), 0, 300));
}
if (actualLine.StartsWith("G92 Z0"))
{
testStream.SetPrinterPosition(new PrinterMove(new Vector3(), 0, 0));
}
if (actualLine == "; do_resume")
{
PauseHandlingStream pauseStream = null;
foreach (var stream in streamList)
{
if (stream as PauseHandlingStream != null)
{
pauseStream = (PauseHandlingStream)stream;
pauseStream.Resume();
}
}
}
if (expectedLine != actualLine)
{
int a = 0;
}
Debug.WriteLine(actualLine);
Assert.AreEqual(expectedLine, actualLine, "Unexpected response from testStream");
// Advance
actualLine = testStream.ReadLine();
if (lineIndex < expected.Length)
{
expectedLine = expected[lineIndex++];
}
}
}
[Test, ChildProcessTest]
public void KnownLayerLinesTest()
{
Assert.AreEqual(8, GCodeFile.GetLayerNumber("; layer 8, Z = 0.800"), "Simplify3D ~ 2019");
Assert.AreEqual(1, GCodeFile.GetLayerNumber("; LAYER:1"), "Cura/MatterSlice");
Assert.AreEqual(7, GCodeFile.GetLayerNumber(";LAYER:7"), "Slic3r Prusa Edition 1.38.7-prusa3d on 2018-04-25");
}
[Test, ChildProcessTest]
public void WriteReplaceStreamTests()
{
string[] inputLines = new string[]
{
"; the printer is moving normally",
"G1 X10 Y10 Z10 E0",
"M114",
"G29",
"G28",
"G28 X0",
"M107",
"M107 ; extra stuff",
};
string[] expected = new string[]
{
"; the printer is moving normally",
"G1 X10 Y10 Z10 E0",
"M114",
"G29",
"G28",
"M115",
"G28 X0",
"M115",
"; none",
"; none ; extra stuff",
};
var printer = new PrinterConfig(new PrinterSettings());
printer.Settings.SetValue(SettingsKey.write_regex, "\"^(G28)\",\"G28,M115\"\\n\"^(M107)\",\"; none\"");
var inputLinesStream = new TestGCodeStream(printer, inputLines);
var queueStream = new QueuedCommandsStream(printer, inputLinesStream);
var writeStream = new ProcessWriteRegexStream(printer, queueStream, queueStream);
ValidateStreamResponse(expected, writeStream);
}
[Test, ChildProcessTest]
public void FeedRateRatioChangesFeedRate()
{
string line;
PrinterConfig printer = new PrinterConfig(new PrinterSettings());
var gcodeStream = new FeedRateMultiplierStream(printer, new TestGCodeStream(printer, new string[] { "G1 X10 F1000", "G1 Y5 F1000" }));
Assert.AreEqual(1, (int)gcodeStream.FeedRateRatio, "FeedRateRatio should default to 1");
line = gcodeStream.ReadLine();
Assert.AreEqual("G1 X10 F1000", line, "FeedRate should remain unchanged when FeedRateRatio is 1.0");
gcodeStream.FeedRateRatio = 2;
line = gcodeStream.ReadLine();
Assert.AreEqual("G1 Y5 F2000", line, "FeedRate should scale from F1000 to F2000 when FeedRateRatio is 2x");
}
[Test, ChildProcessTest]
public void ExtrusionRatioChangesExtrusionAmount()
{
string line;
PrinterConfig printer = new PrinterConfig(new PrinterSettings());
var gcodeStream = new ExtrusionMultiplierStream(printer, new TestGCodeStream(printer, new string[] { "G1 E10", "G1 E0 ; Move back to 0", "G1 E12" }));
Assert.AreEqual(1, (int)gcodeStream.ExtrusionRatio, "ExtrusionRatio should default to 1");
line = gcodeStream.ReadLine();
// Move back to E0
gcodeStream.ReadLine();
Assert.AreEqual("G1 E10", line, "ExtrusionMultiplier should remain unchanged when FeedRateRatio is 1.0");
gcodeStream.ExtrusionRatio = 2;
line = gcodeStream.ReadLine();
Assert.AreEqual("G1 E24", line, "ExtrusionMultiplier should scale from E12 to E24 when ExtrusionRatio is 2x");
}
}
public class TestGCodeStream : GCodeStream
{
private int index = 0;
private string[] lines;
public TestGCodeStream(PrinterConfig printer, string[] lines)
: base(printer)
{
this.lines = lines;
}
public override void Dispose()
{
}
public override string ReadLine()
{
return index < lines.Length ? lines[index++] : null;
}
public override void SetPrinterPosition(PrinterMove position)
{
}
public override GCodeStream InternalStream => null;
public override string DebugInfo => "";
}
}

View file

@ -0,0 +1,78 @@
using System.Collections.Generic;
using MatterHackers.Agg;
using MatterHackers.Agg.Platform;
using MatterHackers.MatterControl.SlicerConfiguration;
using MatterHackers.MatterControl.Tests.Automation;
using NUnit.Framework;
namespace MatterControl.Tests.MatterControl
{
[TestFixture, Category("ConfigIni")]
public class ImportSettingsTests
{
[Test]
public void CheckImportPrinterSettingsToPrinter()
{
StaticData.RootPath = MatterControlUtilities.StaticDataPath;
MatterControlUtilities.OverrideAppDataLocation(MatterControlUtilities.RootPath);
var printerSettings = new PrinterSettings();
printerSettings.SetValue(SettingsKey.cancel_gcode, "cancel gcode");
printerSettings.SetValue(SettingsKey.start_gcode, "start gcode");
string newValue = "----- cancel gcode ----";
string notAnExistingKey = "NotAnExistingKey";
var toImport = new PrinterSettings();
toImport.SetValue(SettingsKey.cancel_gcode, newValue);
toImport.SetValue(notAnExistingKey, "------------------");
var sourceFilter = new List<PrinterSettingsLayer>()
{
toImport.UserLayer
};
printerSettings.Merge(printerSettings.UserLayer, toImport, sourceFilter, false);
Assert.AreEqual(printerSettings.GetValue(SettingsKey.cancel_gcode), newValue, "Imported setting applied");
Assert.IsEmpty(printerSettings.GetValue(notAnExistingKey), "Invalid settings keys should be skipped");
}
[Test]
public void MergeDropsFieldsIfValueAlreadySet()
{
// Validates that field are dropped during import if they are already set in a base layer
//
StaticData.RootPath = MatterControlUtilities.StaticDataPath;
MatterControlUtilities.OverrideAppDataLocation(MatterControlUtilities.RootPath);
var printerSettings = new PrinterSettings();
printerSettings.SetValue(SettingsKey.cancel_gcode, "cancel gcode");
printerSettings.SetValue(SettingsKey.start_gcode, "start gcode");
// Ensure layer_height of a given value
printerSettings.BaseLayer[SettingsKey.layer_height] = "0.25";
string newValue = "----- cancel gcode ----";
string notAnExistingKey = "NotAnExistingKey";
var toImport = new PrinterSettings();
toImport.SetValue(SettingsKey.cancel_gcode, newValue);
toImport.SetValue(SettingsKey.layer_height, "0.25");
toImport.SetValue(notAnExistingKey, "------------------");
var sourceFilter = new List<PrinterSettingsLayer>()
{
toImport.UserLayer
};
printerSettings.Merge(printerSettings.UserLayer, toImport, sourceFilter, false);
Assert.AreEqual(printerSettings.GetValue(SettingsKey.cancel_gcode), newValue, "Imported setting applied");
Assert.IsEmpty(printerSettings.GetValue(notAnExistingKey), "Invalid settings keys should be skipped");
Assert.IsFalse(printerSettings.UserLayer.ContainsKey(SettingsKey.layer_height), "User layer should not contain layer_height after merge");
Assert.AreEqual(2, printerSettings.UserLayer.Count, "User layer should contain two items after import (start_gcode, cancel_gcode)");
}
}
}

View file

@ -0,0 +1,820 @@
/*
Copyright (c) 2016, 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 MatterHackers.Agg;
using MatterHackers.Agg.Platform;
using MatterHackers.Agg.UI;
using MatterHackers.DataConverters3D;
using MatterHackers.MatterControl;
using MatterHackers.MatterControl.DesignTools;
using MatterHackers.MatterControl.DesignTools.Operations;
using MatterHackers.MatterControl.PartPreviewWindow.View3D;
using MatterHackers.MatterControl.Tests.Automation;
using MatterHackers.PolygonMesh;
using MatterHackers.PolygonMesh.Processors;
using MatterHackers.VectorMath;
using NUnit.Framework;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using TestInvoker;
namespace MatterControl.Tests.MatterControl
{
[TestFixture]
public class InteractiveSceneTests
{
public static void StartupMC()
{
StaticData.RootPath = MatterControlUtilities.StaticDataPath;
MatterControlUtilities.OverrideAppDataLocation(MatterControlUtilities.RootPath);
// Automation runner must do as much as program.cs to spin up platform
string platformFeaturesProvider = "MatterHackers.MatterControl.WindowsPlatformsFeatures, MatterControl.Winforms";
AppContext.Platform = AggContext.CreateInstanceFrom<INativePlatformFeatures>(platformFeaturesProvider);
AppContext.Platform.InitPluginFinder();
AppContext.Platform.ProcessCommandline();
}
[Test, Category("InteractiveScene")]
public async Task CombineTests()
{
StartupMC();
// Combine has correct results
{
var root = new Object3D();
var cubeA = await CubeObject3D.Create(20, 20, 20);
var cubeB = await CubeObject3D.Create(20, 20, 20);
var offsetCubeB = new TranslateObject3D(cubeB, 10);
Assert.IsTrue(offsetCubeB.GetAxisAlignedBoundingBox().Equals(new AxisAlignedBoundingBox(
0, -10, -10,
20, 10, 10), .001));
var union = new CombineObject3D_2();
union.Children.Add(cubeA);
union.Children.Add(offsetCubeB);
root.Children.Add(union);
Assert.IsTrue(union.GetAxisAlignedBoundingBox().Equals(new AxisAlignedBoundingBox(
-10, -10, -10,
20, 10, 10), .001));
Assert.IsTrue(root.GetAxisAlignedBoundingBox().Equals(new AxisAlignedBoundingBox(
-10, -10, -10,
20, 10, 10), .001));
union.Combine();
Assert.IsTrue(union.GetAxisAlignedBoundingBox().Equals(new AxisAlignedBoundingBox(
-10, -10, -10,
20, 10, 10), .001));
union.Apply(null);
Assert.AreEqual(1, root.Children.Count());
var rootAabb = root.GetAxisAlignedBoundingBox();
Assert.IsTrue(new AxisAlignedBoundingBox(
-10, -10, -10,
20, 10, 10).Equals(rootAabb, .001));
}
// Combine has correct results when inner content is changed
{
var root = new Object3D();
var cubeA = await CubeObject3D.Create(20, 20, 20);
var cubeB = await CubeObject3D.Create(20, 20, 20);
var union = new CombineObject3D_2();
union.Children.Add(cubeA);
union.Children.Add(cubeB);
root.Children.Add(union);
union.Combine();
Assert.AreEqual(5, root.Descendants().Count());
var rootAabb = root.GetAxisAlignedBoundingBox();
Assert.IsTrue(new AxisAlignedBoundingBox(
-10, -10, -10,
10, 10, 10).Equals(rootAabb, .001));
var offsetCubeB = new TranslateObject3D(cubeB, 10);
union.Combine();
Assert.AreEqual(7, root.Descendants().Count());
rootAabb = root.GetAxisAlignedBoundingBox();
Assert.IsTrue(new AxisAlignedBoundingBox(
-10, -10, -10,
20, 10, 10).Equals(rootAabb, .001));
}
// now make sure undo has the right results for flatten
{
var root = new Object3D();
var cubeA = await CubeObject3D.Create(20, 20, 20);
var cubeB = await CubeObject3D.Create(20, 20, 20);
var offsetCubeB = new TranslateObject3D(cubeB, 10);
var combine = new CombineObject3D_2();
combine.Children.Add(cubeA);
combine.Children.Add(offsetCubeB);
root.Children.Add(combine);
Assert.AreEqual(5, root.Descendants().Count(), "Should have the 1 combine, 2 cubeA, 3 cubeB, 4 offset cubeB, 5 offset sourceItem");
combine.Combine();
Assert.AreEqual(7, root.Descendants().Count(), "Should have the 1 combine, 2 cubeA, 3 wrapped cubeA, 4 cubeB, 5 offset cubeB, 6 offset sourceItem, wrapped cubeB");
var rootAabb = root.GetAxisAlignedBoundingBox();
Assert.IsTrue(new AxisAlignedBoundingBox(
-10, -10, -10,
20, 10, 10).Equals(rootAabb, .001));
var undoBuffer = new UndoBuffer();
combine.Apply(undoBuffer);
Assert.AreEqual(1, root.Descendants().Count());
Assert.AreEqual(1, root.Children.Count());
rootAabb = root.GetAxisAlignedBoundingBox();
Assert.IsTrue(new AxisAlignedBoundingBox(
-10, -10, -10,
20, 10, 10).Equals(rootAabb, .001));
undoBuffer.Undo();
Assert.AreEqual(7, root.Descendants().Count(), "Should have the 1 combine, 2 cubeA, 3 wrapped cubeA, 4 cubeB, 5 offset cubeB, 6 offset sourceItem, wrapped cubeB");
}
// now make sure undo has the right results for remove
{
var root = new Object3D();
var cubeA = await CubeObject3D.Create(20, 20, 20);
cubeA.Name = "cubeA";
var cubeB = await CubeObject3D.Create(20, 20, 20);
cubeB.Name = "cubeB";
var combine = new CombineObject3D_2();
combine.Children.Add(cubeA);
combine.Children.Add(cubeB);
root.Children.Add(combine);
Assert.AreEqual(3, root.Descendants().Count(), "Should have the 1 combine, 2 cubeA, 3 cubeB");
combine.Combine();
Assert.AreEqual(5, root.Descendants().Count(), "Should have the 1 combine, 2 cubeA, 3 wrapped cubeA, 4 cubeB, 5 wrapped cubeB");
var rootAabb = root.GetAxisAlignedBoundingBox();
Assert.IsTrue(new AxisAlignedBoundingBox(
-10, -10, -10,
10, 10, 10).Equals(rootAabb, .001));
var undoBuffer = new UndoBuffer();
combine.Cancel(undoBuffer);
Assert.AreEqual(2, root.Descendants().Count(), "Should have the 1 cubeA, 2 cubeB");
rootAabb = root.GetAxisAlignedBoundingBox();
Assert.IsTrue(new AxisAlignedBoundingBox(
-10, -10, -10,
10, 10, 10).Equals(rootAabb, .001));
undoBuffer.Undo();
Assert.AreEqual(5, root.Descendants().Count(), "Should have the 1 combine, 2 cubeA, 3 wrapped cubeA, 4 cubeB, 5 wrapped cubeB");
}
// now make sure undo has the right results for remove
{
var root = new Object3D();
var cubeA = await CubeObject3D.Create(20, 20, 20);
var cubeB = await CubeObject3D.Create(20, 20, 20);
var offsetCubeB = new TranslateObject3D(cubeB, 10);
var combine = new CombineObject3D_2();
combine.Children.Add(cubeA);
combine.Children.Add(offsetCubeB);
root.Children.Add(combine);
Assert.AreEqual(5, root.Descendants().Count(), "Should have the 1 combine, 2 cubeA, 3 cubeB, 4 offset cubeB, 5 offset sourceItem");
combine.Combine();
Assert.AreEqual(7, root.Descendants().Count(), "Should have the 1 combine, 2 cubeA, 3 wrapped cubeA, 4 cubeB, 5 offset cubeB, 6 offset sourceItem, wrapped cubeB");
var rootAabb = root.GetAxisAlignedBoundingBox();
Assert.IsTrue(new AxisAlignedBoundingBox(
-10, -10, -10,
20, 10, 10).Equals(rootAabb, .001));
var undoBuffer = new UndoBuffer();
combine.Cancel(undoBuffer);
Assert.AreEqual(4, root.Descendants().Count(), "Should have the 1 cubeA, 2 cubeB, 3 offset cubeB, 4 offset sourceItem");
rootAabb = root.GetAxisAlignedBoundingBox();
Assert.IsTrue(new AxisAlignedBoundingBox(
-10, -10, -10,
20, 10, 10).Equals(rootAabb, .001));
undoBuffer.Undo();
Assert.AreEqual(7, root.Descendants().Count(), "Should have the 1 combine, 2 cubeA, 3 wrapped cubeA, 4 cubeB, 5 offset cubeB, 6 offset sourceItem, wrapped cubeB");
}
// make sure the MatterCAD add function is working
{
var cubeA = await CubeObject3D.Create(20, 20, 20);
var cubeB = await CubeObject3D.Create(20, 20, 20);
var offsetCubeB = new TranslateObject3D(cubeB, 10);
var plus = cubeA.Plus(offsetCubeB, true);
Assert.AreEqual(0, plus.Children.Count());
Assert.IsTrue(plus.Mesh != null);
var aabb = plus.GetAxisAlignedBoundingBox();
Assert.IsTrue(new AxisAlignedBoundingBox(
-10, -10, -10,
20, 10, 10).Equals(aabb, .001));
}
// test single object combine
{
var root = new Object3D();
var cubeA = await CubeObject3D.Create(20, 20, 20);
var cubeB = await CubeObject3D.Create(20, 20, 20);
var offsetCubeB = new TranslateObject3D(cubeB, 10);
var group = new Object3D();
group.Children.Add(cubeA);
group.Children.Add(offsetCubeB);
var union = new CombineObject3D_2();
union.Children.Add(group);
root.Children.Add(union);
union.Combine();
Assert.AreEqual(8, root.Descendants().Count(), "group, union, wa, a, wtb, tb, b");
union.Apply(null);
Assert.AreEqual(1, root.Descendants().Count(), "Should have the union result");
Assert.AreEqual(1, root.Children.Count());
var rootAabb = root.GetAxisAlignedBoundingBox();
Assert.IsTrue(new AxisAlignedBoundingBox(
-10, -10, -10,
20, 10, 10).Equals(rootAabb, .001));
}
}
[Test, Category("InteractiveScene")]
public async Task SubtractTests()
{
StartupMC();
// Subtract has correct number of results
{
var root = new Object3D();
var cubeA = await CubeObject3D.Create(20, 20, 20);
var cubeB = await CubeObject3D.Create(20, 20, 20);
var offsetCubeB = new TranslateObject3D(cubeB, 10);
var subtract = new SubtractObject3D_2();
subtract.Children.Add(cubeA);
subtract.Children.Add(offsetCubeB);
subtract.SelectedChildren.Add(offsetCubeB.ID);
root.Children.Add(subtract);
subtract.Subtract();
subtract.Apply(null);
Assert.AreEqual(1, root.Children.Count());
var rootAabb = root.GetAxisAlignedBoundingBox();
Assert.IsTrue(new AxisAlignedBoundingBox(
-10, -10, -10,
0, 10, 10).Equals(rootAabb, .001));
}
// make sure the MatterCAD subtract function is working
{
var cubeA = await CubeObject3D.Create(20, 20, 20);
var cubeB = await CubeObject3D.Create(20, 20, 20);
var offsetCubeB = new TranslateObject3D(cubeB, 10);
var subtract = cubeA.Minus(offsetCubeB);
Assert.AreEqual(0, subtract.Children.Count());
Assert.IsTrue(subtract.Mesh != null);
var aabb = subtract.GetAxisAlignedBoundingBox();
Assert.IsTrue(new AxisAlignedBoundingBox(
-10, -10, -10,
0, 10, 10).Equals(aabb, .001));
}
}
[Test, Category("InteractiveScene")]
public async Task HoleTests()
{
StartupMC();
// Subtract has correct number of results with combine
{
var root = new Object3D();
var cubeA1 = await CubeObject3D.Create(20, 20, 20);
var cubeA2 = await CubeObject3D.Create(20, 20, 20);
var cubeB = await CubeObject3D.Create(20, 20, 20);
var offsetCubeB = new TranslateObject3D(cubeB, 0, 10);
var align = new AlignObject3D_2();
align.Children.Add(cubeA1);
align.Children.Add(cubeA2);
align.OutputType = PrintOutputTypes.Hole;
await align.Rebuild();
var combine = new CombineObject3D_2();
combine.Children.Add(align);
combine.Children.Add(offsetCubeB);
root.Children.Add(combine);
await combine.Rebuild();
Assert.AreEqual(1, root.Children.Count());
var rootAabb = root.GetAxisAlignedBoundingBox();
Assert.AreEqual(10, rootAabb.YSize, .001);
}
// Subtract has correct number of results with group
{
var root = new Object3D();
var cubeA1 = await CubeObject3D.Create(20, 20, 20);
var cubeA2 = await CubeObject3D.Create(20, 20, 20);
var cubeB = await CubeObject3D.Create(20, 20, 20);
var offsetCubeB = new TranslateObject3D(cubeB, 0, 10);
var align = new AlignObject3D_2();
align.Children.Add(cubeA1);
align.Children.Add(cubeA2);
align.OutputType = PrintOutputTypes.Hole;
await align.Rebuild();
var combine = new GroupHolesAppliedObject3D();
combine.Children.Add(align);
combine.Children.Add(offsetCubeB);
root.Children.Add(combine);
await combine.Rebuild();
Assert.AreEqual(1, root.Children.Count());
var rootAabb = root.GetAxisAlignedBoundingBox();
Assert.AreEqual(10, rootAabb.YSize, .001);
}
}
[Test, Category("InteractiveScene")]
public async Task CopyObjectTests()
{
StartupMC();
// Copying an object copies the internal state correctly
{
var cubeA1 = await CubeObject3D.Create(10, 20, 20);
var copy = cubeA1.Clone() as CubeObject3D;
Assert.AreEqual("10", copy.Width.Expression, "10");
Assert.AreEqual(10, copy.GetAxisAlignedBoundingBox().XSize, .001);
}
}
[Test, Category("InteractiveScene")]
public async Task AabbCalculatedCorrectlyForPinchedFitObjects()
{
StartupMC();
// build without pinch
{
var root = new Object3D();
var cube = await CubeObject3D.Create(20, 20, 20);
root.Children.Add(cube);
Assert.IsTrue(root.GetAxisAlignedBoundingBox().Equals(new AxisAlignedBoundingBox(new Vector3(-10, -10, -10), new Vector3(10, 10, 10)), .001));
root.Children.Remove(cube);
var fit = await FitToBoundsObject3D_4.Create(cube);
fit.Width = 50;
fit.Depth = 20;
fit.Height = 20;
fit.Invalidate(InvalidateType.Properties);
root.Children.Add(fit);
var rootAabb = root.GetAxisAlignedBoundingBox();
Assert.IsTrue(rootAabb.Equals(new AxisAlignedBoundingBox(new Vector3(-25, -10, -10), new Vector3(25, 10, 10)), .001));
}
// build with pinch
{
var root = new Object3D();
var cube = await CubeObject3D.Create(20, 20, 20);
var fit = await FitToBoundsObject3D_4.Create(cube);
fit.Width = 50;
fit.Depth = 20;
fit.Height = 20;
var pinch = new PinchObject3D_3();
pinch.Children.Add(fit);
await pinch.Rebuild();
root.Children.Add(pinch);
var rootAabb = root.GetAxisAlignedBoundingBox();
Assert.IsTrue(rootAabb.Equals(new AxisAlignedBoundingBox(new Vector3(-10, -10, -10), new Vector3(10, 10, 10)), .001));
}
// build with translate
{
var root = new Object3D();
var cube = await CubeObject3D.Create(20, 20, 20);
var translate = new TranslateObject3D(cube, 11, 0, 0);
root.Children.Add(translate);
var rootAabb = root.GetAxisAlignedBoundingBox();
Assert.IsTrue(rootAabb.Equals(new AxisAlignedBoundingBox(new Vector3(1, -10, -10), new Vector3(21, 10, 10)), .001));
}
// build with pinch and translate
{
var root = new Object3D();
var cube = await CubeObject3D.Create(20, 20, 20);
var translate = new TranslateObject3D(cube, 11, 0, 0);
var pinch = new PinchObject3D_3();
pinch.Children.Add(translate);
root.Children.Add(pinch);
await pinch.Rebuild();
var rootAabb = root.GetAxisAlignedBoundingBox();
Assert.IsTrue(rootAabb.Equals(new AxisAlignedBoundingBox(new Vector3(1, -10, -10), new Vector3(21, 10, 10)), .001));
}
// build with pinch and translate
{
var root = new Object3D();
var cube = await CubeObject3D.Create(20, 20, 20);
var fit = await FitToBoundsObject3D_4.Create(cube);
fit.Width = 50;
fit.Depth = 20;
fit.Height = 20;
var translate = new TranslateObject3D(fit, 11, 0, 0);
var pinch = new PinchObject3D_3();
pinch.Children.Add(translate);
await pinch.Rebuild();
root.Children.Add(pinch);
var rootAabb = root.GetAxisAlignedBoundingBox();
Assert.IsTrue(rootAabb.Equals(new AxisAlignedBoundingBox(new Vector3(1, -10, -10), new Vector3(21, 10, 10)), .001));
}
}
[Test, Category("InteractiveScene")]
public async Task ScaleObjectMaintainsCorrectAabb()
{
// build cube with scale and undo
{
// create a simple cube with translation
var root = new Object3D();
var cube = await CubeObject3D.Create(20, 20, 20);
cube.Matrix = Matrix4X4.CreateTranslation(50, 60, 10);
root.Children.Add(cube);
Assert.AreEqual(2, root.DescendantsAndSelf().Count());
var preScaleAabb = root.GetAxisAlignedBoundingBox();
var undoBuffer = new UndoBuffer();
// add a scale to it (that is not scaled)
var scaleObject = new ScaleObject3D_3();
scaleObject.WrapItems(new IObject3D[] { cube }, undoBuffer);
// ensure that the object did not move
Assert.AreEqual(4, root.DescendantsAndSelf().Count());
var postScaleAabb = root.GetAxisAlignedBoundingBox();
Assert.IsTrue(preScaleAabb.Equals(postScaleAabb, .001));
Assert.AreNotEqual(cube, scaleObject.UntransformedChildren.First(), "There is an undo buffer, there should have been a clone");
}
// build cube with scale
{
// create a simple cube with translation
var root = new Object3D();
var cube = await CubeObject3D.Create(20, 20, 20);
cube.Matrix = Matrix4X4.CreateTranslation(50, 60, 10);
root.Children.Add(cube);
Assert.AreEqual(2, root.DescendantsAndSelf().Count());
var preScaleAabb = root.GetAxisAlignedBoundingBox();
// add a scale to it (that is not scaled)
var scaleObject = new ScaleObject3D_3(cube);
// ensure that the object did not move
Assert.AreEqual(4, root.DescendantsAndSelf().Count());
var postScaleAabb = root.GetAxisAlignedBoundingBox();
Assert.IsTrue(preScaleAabb.Equals(postScaleAabb, .001));
Assert.AreEqual(cube, scaleObject.UntransformedChildren.First(), "There is no undo buffer, there should not have been a clone");
}
}
[Test, Category("InteractiveScene")]
public async Task ScaleAndRotateMaintainsCorrectAabb()
{
{
// create a simple cube with translation
var root = new Object3D();
var cube = await CubeObject3D.Create(20, 20, 20);
cube.Matrix = Matrix4X4.CreateTranslation(50, 60, 10);
root.Children.Add(cube);
Assert.AreEqual(2, root.DescendantsAndSelf().Count());
var preScaleAabb = root.GetAxisAlignedBoundingBox();
// add a scale to it (that is not scaled)
var scaleObject = new ScaleObject3D_3(cube);
// ensure that the object did not move
Assert.AreEqual(4, root.DescendantsAndSelf().Count());
var postScaleAabb = root.GetAxisAlignedBoundingBox();
Assert.IsTrue(preScaleAabb.Equals(postScaleAabb, .001));
Assert.AreEqual(cube, scaleObject.UntransformedChildren.First(), "There is no undo buffer, there should not have been a clone");
var rotateScaleObject = new RotateObject3D_2(cube);
// ensure that the object did not move
Assert.AreEqual(6, root.DescendantsAndSelf().Count());
var postRotateScaleAabb = root.GetAxisAlignedBoundingBox();
Assert.IsTrue(preScaleAabb.Equals(postRotateScaleAabb, .001));
Assert.AreEqual(cube, rotateScaleObject.UntransformedChildren.First(), "There is no undo buffer, there should not have been a clone");
}
}
[Test, Category("InteractiveScene")]
public async Task RotateMaintainsCorrectAabb()
{
{
// create a simple cube with translation
var root = new Object3D();
var cube = await CubeObject3D.Create(20, 20, 20);
cube.Matrix = Matrix4X4.CreateTranslation(50, 60, 10);
root.Children.Add(cube);
Assert.AreEqual(2, root.DescendantsAndSelf().Count());
var preRotateAabb = root.GetAxisAlignedBoundingBox();
// add a rotate to it (that is not rotated)
var rotateObject = new RotateObject3D_2(cube);
// ensure that the object did not move
Assert.IsTrue(rotateObject.RotateAbout.Origin.Equals(new Vector3(50, 60, 10)));
Assert.AreEqual(4, root.DescendantsAndSelf().Count());
var postRotateAabb = root.GetAxisAlignedBoundingBox();
Assert.IsTrue(preRotateAabb.Equals(postRotateAabb, .001));
Assert.AreEqual(cube, rotateObject.UntransformedChildren.First(), "There is no undo buffer, there should not have been a clone");
}
}
[Test, Category("InteractiveScene")]
public async Task AabbCalculatedCorrectlyForAlignedFitObject()
{
StartupMC();
var root = new Object3D();
var cube = await CubeObject3D.Create(20, 20, 20);
var fit = await FitToBoundsObject3D_4.Create(cube);
fit.Width = 10;
fit.Depth = 10;
fit.Height = 6;
fit.Invalidate(InvalidateType.Properties);
Assert.IsTrue(fit.GetAxisAlignedBoundingBox().Equals(new AxisAlignedBoundingBox(new Vector3(-5, -5, -10), new Vector3(5, 5, -4)), .01));
var bigCube = await CubeObject3D.Create(20, 20, 20);
Assert.IsTrue(bigCube.GetAxisAlignedBoundingBox().Equals(new AxisAlignedBoundingBox(new Vector3(-10, -10, -10), new Vector3(10, 10, 10)), .01));
var align = new AlignObject3D_2();
align.Children.Add(bigCube);
align.Children.Add(fit);
await align.Rebuild();
align.XAlign = Align.Center;
align.YAlign = Align.Center;
align.ZAlign = Align.Max;
align.XOptions = true;
align.YOptions = true;
align.ZOptions = true;
align.ZOffset = 1;
await align.Rebuild();
var alignAabb = align.GetAxisAlignedBoundingBox();
Assert.IsTrue(alignAabb.Equals(new AxisAlignedBoundingBox(new Vector3(-10, -10, -10), new Vector3(10, 10, 11)), .01));
alignAabb = align.GetAxisAlignedBoundingBox();
root.Children.Add(align);
var rootAabb = root.GetAxisAlignedBoundingBox();
Assert.IsTrue(rootAabb.Equals(new AxisAlignedBoundingBox(new Vector3(-10, -10, -10), new Vector3(10, 10, 11)), .01));
}
[Test]
public void CombineTests2()
{
// overlaping results in simple new mesh
{
var meshA = PlatonicSolids.CreateCube(10, 10, 10);
meshA.Translate(0, 0, 0);
var meshB = PlatonicSolids.CreateCube(10, 10, 10);
meshB.Translate(0, 5, 0);
var mesh = Object3D.CombineParticipants(null,
new IObject3D[]
{
new Object3D() { Mesh = meshA },
new Object3D() { Mesh = meshB },
},
new CancellationToken());
Assert.AreEqual(12, mesh.Faces.Count());
var aabb = mesh.GetAxisAlignedBoundingBox();
Assert.AreEqual(15, aabb.YSize, .001);
}
// multiple overlaping faces all combine
{
var meshA = PlatonicSolids.CreateCube(10, 10, 10);
meshA.Translate(0, 0, 0);
var meshB = PlatonicSolids.CreateCube(10, 10, 10);
meshB.Translate(0, -3, 0);
var meshC = PlatonicSolids.CreateCube(10, 10, 10);
meshC.Translate(0, 3, 0);
var mesh = Object3D.CombineParticipants(null,
new IObject3D[]
{
new Object3D() { Mesh = meshA },
new Object3D() { Mesh = meshB },
new Object3D() { Mesh = meshC },
},
new CancellationToken());
Assert.AreEqual(12, mesh.Faces.Count());
var aabb = mesh.GetAxisAlignedBoundingBox();
Assert.AreEqual(16, aabb.YSize, .001);
}
// a back face against a front face, both are removed
{
var meshA = PlatonicSolids.CreateCube(10, 10, 10);
meshA.Translate(0, -5, 0);
var meshB = PlatonicSolids.CreateCube(10, 10, 10);
meshB.Translate(0, 5, 0);
var mesh = Object3D.CombineParticipants(null,
new IObject3D[]
{
new Object3D() { Mesh = meshA },
new Object3D() { Mesh = meshB },
},
new CancellationToken());
// StlProcessing.Save(mesh, @"C:\temp\temp.stl", new CancellationToken());
Assert.AreEqual(12, mesh.Faces.Count());
var aabb = mesh.GetAxisAlignedBoundingBox();
Assert.AreEqual(20, aabb.YSize, .001);
}
// multiple overlaping faces all combine
{
// right side at 0
var meshA = PlatonicSolids.CreateCube(10, 5, 10);
meshA.Translate(-5, 0, 0);
// left side at -5
var meshB = PlatonicSolids.CreateCube(10, 5, 10);
meshB.Translate(0, 0, 0);
// right side at 0
var meshC = PlatonicSolids.CreateCube(5, 10, 10);
meshC.Translate(-2.5, 0, 0);
var mesh = Object3D.CombineParticipants(null,
new IObject3D[]
{
new Object3D() { Mesh = meshA },
new Object3D() { Mesh = meshB },
new Object3D() { Mesh = meshC },
},
new CancellationToken());
// StlProcessing.Save(mesh, @"C:\temp\temp.stl", new CancellationToken());
Assert.AreEqual(44, mesh.Faces.Count());
var aabb = mesh.GetAxisAlignedBoundingBox();
Assert.AreEqual(10, aabb.YSize, .001);
}
}
[Test, Category("InteractiveScene")]
public async Task AlignObjectHasCorrectPositionsOnXAxis()
{
StartupMC();
var scene = new InteractiveScene();
var cube = await CubeObject3D.Create(20, 20, 20);
cube.Matrix = Matrix4X4.CreateTranslation(50, 60, 10);
Assert.IsTrue(cube.GetAxisAlignedBoundingBox().Equals(new AxisAlignedBoundingBox(new Vector3(40, 50, 0), new Vector3(60, 70, 20)), .01));
scene.Children.Add(cube);
var bigCube = await CubeObject3D.Create(40, 40, 40);
bigCube.Matrix = Matrix4X4.CreateTranslation(20, 20, 20);
Assert.IsTrue(bigCube.GetAxisAlignedBoundingBox().Equals(new AxisAlignedBoundingBox(new Vector3(0, 0, 0), new Vector3(40, 40, 40)), .01));
scene.Children.Add(bigCube);
// select them
scene.SelectedItem = cube;
scene.AddToSelection(bigCube);
// create an align of them
var align = new AlignObject3D_2();
align.AddSelectionAsChildren(scene, scene.SelectedItem);
var unalignedBounds = align.GetAxisAlignedBoundingBox();
// assert the align in built correctly
Assert.AreEqual(1, scene.Children.Count);
Assert.AreEqual(2, align.Children.Count);
Assert.IsTrue(align.GetAxisAlignedBoundingBox().Equals(new AxisAlignedBoundingBox(new Vector3(0, 0, 0), new Vector3(60, 70, 40)), 1.0));
align.SelectedChild = new SelectedChildren() { cube.ID.ToString() };
Assert.IsTrue(align.GetAxisAlignedBoundingBox().Equals(unalignedBounds, 1.0));
// turn align on
align.XAlign = Align.Min;
await align.Rebuild();
Assert.IsTrue(align.GetAxisAlignedBoundingBox().Equals(new AxisAlignedBoundingBox(new Vector3(40, 0, 0), new Vector3(80, 70, 40)), 1.0));
// turn it off
align.XAlign = Align.None;
await align.Rebuild();
Assert.IsTrue(align.GetAxisAlignedBoundingBox().Equals(unalignedBounds, 1.0));
// turn it back on
align.XAlign = Align.Min;
await align.Rebuild();
Assert.IsTrue(align.GetAxisAlignedBoundingBox().Equals(new AxisAlignedBoundingBox(new Vector3(40, 0, 0), new Vector3(80, 70, 40)), 1.0));
// remove the align and assert stuff moved back to where it started
align.Cancel(null);
Assert.IsTrue(cube.GetAxisAlignedBoundingBox().Equals(new AxisAlignedBoundingBox(new Vector3(40, 50, 0), new Vector3(60, 70, 20)), .01));
Assert.IsTrue(bigCube.GetAxisAlignedBoundingBox().Equals(new AxisAlignedBoundingBox(new Vector3(0, 0, 0), new Vector3(40, 40, 40)), .01));
}
[Test, Category("InteractiveScene")]
public async Task AabbCalculatedCorrectlyForCurvedFitObjects()
{
StartupMC();
var root = new Object3D();
var cube = await CubeObject3D.Create(20, 20, 20);
var fit = await FitToBoundsObject3D_4.Create(cube);
fit.Width = 50;
fit.Depth = 20;
fit.Height = 20;
fit.Invalidate(InvalidateType.Properties);
Assert.IsTrue(fit.GetAxisAlignedBoundingBox().Equals(new AxisAlignedBoundingBox(new Vector3(-25, -10, -10), new Vector3(25, 10, 10)), 1.0));
var curve = new CurveObject3D_3()
{
BendType = CurveObject3D_3.BendTypes.Diameter,
Diameter = 50
};
curve.Children.Add(fit);
await curve.Rebuild();
var curveAabb = curve.GetAxisAlignedBoundingBox();
root.Children.Add(curve);
var rootAabb = root.GetAxisAlignedBoundingBox();
Assert.IsTrue(rootAabb.Equals(new AxisAlignedBoundingBox(new Vector3(-17.5, -9.9, -10), new Vector3(17.5, 11.97, 10)), 1.0));
}
}
}

View file

@ -0,0 +1,153 @@
/*
Copyright (c) 2016, 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.Collections.Generic;
using MatterControl.Printing;
using MatterHackers.Agg;
using MatterHackers.Agg.Platform;
using MatterHackers.MatterControl;
using MatterHackers.MatterControl.ConfigurationPage.PrintLeveling;
using MatterHackers.MatterControl.SlicerConfiguration;
using MatterHackers.MatterControl.Tests.Automation;
using MatterHackers.VectorMath;
using NUnit.Framework;
namespace MatterControl.Tests.MatterControl
{
[TestFixture]
public class LevelingTests
{
[Test, Category("Leveling")]
public void LevelingMesh3x3CorectInterpolation()
{
StaticData.RootPath = MatterControlUtilities.StaticDataPath;
MatterControlUtilities.OverrideAppDataLocation(MatterControlUtilities.RootPath);
var printerSettings = new PrinterSettings();
printerSettings.SetValue(SettingsKey.probe_offset, "0,0,0");
var printer = new PrinterConfig(printerSettings);
// a 2 x 2 mesh that goes form 0 on the left to 10 on the right
{
var levelingData = new PrintLevelingData();
// put them in left to right - bottom to top
levelingData.SampledPositions = new List<Vector3>();
levelingData.SampledPositions.Add(new Vector3(0, 0, 0));
levelingData.SampledPositions.Add(new Vector3(10, 0, 10));
levelingData.SampledPositions.Add(new Vector3(0, 10, 0));
levelingData.SampledPositions.Add(new Vector3(10, 10, 10));
LevelingFunctions levelingFunctionsMesh2x2 = new LevelingFunctions(printer, levelingData);
// check on points
AssertMeshLevelPoint(new Vector3(0, 0, 0), new Vector3(0, 0, 0), levelingFunctionsMesh2x2);
AssertMeshLevelPoint(new Vector3(10, 0, 0), new Vector3(10, 0, 10), levelingFunctionsMesh2x2);
AssertMeshLevelPoint(new Vector3(10, 10, 0), new Vector3(10, 10, 10), levelingFunctionsMesh2x2);
AssertMeshLevelPoint(new Vector3(0, 10, 0), new Vector3(0, 10, 0), levelingFunctionsMesh2x2);
// check raised on points
AssertMeshLevelPoint(new Vector3(0, 0, 5), new Vector3(0, 0, 5), levelingFunctionsMesh2x2);
AssertMeshLevelPoint(new Vector3(10, 0, 5), new Vector3(10, 0, 15), levelingFunctionsMesh2x2);
AssertMeshLevelPoint(new Vector3(10, 10, 5), new Vector3(10, 10, 15), levelingFunctionsMesh2x2);
AssertMeshLevelPoint(new Vector3(0, 10, 5), new Vector3(0, 10, 5), levelingFunctionsMesh2x2);
// check between points
AssertMeshLevelPoint(new Vector3(5, 0, 0), new Vector3(5, 0, 5), levelingFunctionsMesh2x2);
AssertMeshLevelPoint(new Vector3(5, 0, 5), new Vector3(5, 0, 10), levelingFunctionsMesh2x2);
// check outside points
AssertMeshLevelPoint(new Vector3(-5, 0, 0), new Vector3(-5, 0, -5), levelingFunctionsMesh2x2);
AssertMeshLevelPoint(new Vector3(-5, 0, 5), new Vector3(-5, 0, 0), levelingFunctionsMesh2x2);
AssertMeshLevelPoint(new Vector3(15, 0, 0), new Vector3(15, 0, 15), levelingFunctionsMesh2x2);
AssertMeshLevelPoint(new Vector3(15, 0, 5), new Vector3(15, 0, 20), levelingFunctionsMesh2x2);
}
// a 3 x 3 mesh that goes form 0 on the left to 10 on the right
{
var levelingData = new PrintLevelingData();
// put them in left to right - bottom to top
levelingData.SampledPositions = new List<Vector3>();
levelingData.SampledPositions.Add(new Vector3(0, 0, 0));
levelingData.SampledPositions.Add(new Vector3(5, 0, 5));
levelingData.SampledPositions.Add(new Vector3(10, 0, 10));
levelingData.SampledPositions.Add(new Vector3(0, 5, 0));
levelingData.SampledPositions.Add(new Vector3(5, 5, 5));
levelingData.SampledPositions.Add(new Vector3(10, 5, 10));
levelingData.SampledPositions.Add(new Vector3(0, 10, 0));
levelingData.SampledPositions.Add(new Vector3(5, 10, 5));
levelingData.SampledPositions.Add(new Vector3(10, 10, 10));
LevelingFunctions levelingFunctionsMesh2x2 = new LevelingFunctions(printer, levelingData);
// check on points
AssertMeshLevelPoint(new Vector3(0, 0, 0), new Vector3(0, 0, 0), levelingFunctionsMesh2x2);
AssertMeshLevelPoint(new Vector3(10, 0, 0), new Vector3(10, 0, 10), levelingFunctionsMesh2x2);
AssertMeshLevelPoint(new Vector3(10, 10, 0), new Vector3(10, 10, 10), levelingFunctionsMesh2x2);
AssertMeshLevelPoint(new Vector3(0, 10, 0), new Vector3(0, 10, 0), levelingFunctionsMesh2x2);
// check raised on points
AssertMeshLevelPoint(new Vector3(0, 0, 5), new Vector3(0, 0, 5), levelingFunctionsMesh2x2);
AssertMeshLevelPoint(new Vector3(10, 0, 5), new Vector3(10, 0, 15), levelingFunctionsMesh2x2);
AssertMeshLevelPoint(new Vector3(10, 10, 5), new Vector3(10, 10, 15), levelingFunctionsMesh2x2);
AssertMeshLevelPoint(new Vector3(0, 10, 5), new Vector3(0, 10, 5), levelingFunctionsMesh2x2);
// check between points
AssertMeshLevelPoint(new Vector3(5, 0, 0), new Vector3(5, 0, 5), levelingFunctionsMesh2x2);
AssertMeshLevelPoint(new Vector3(5, 0, 5), new Vector3(5, 0, 10), levelingFunctionsMesh2x2);
// check outside points
AssertMeshLevelPoint(new Vector3(-5, 0, 0), new Vector3(-5, 0, -5), levelingFunctionsMesh2x2);
AssertMeshLevelPoint(new Vector3(-5, 0, 5), new Vector3(-5, 0, 0), levelingFunctionsMesh2x2);
AssertMeshLevelPoint(new Vector3(15, 0, 0), new Vector3(15, 0, 15), levelingFunctionsMesh2x2);
AssertMeshLevelPoint(new Vector3(15, 0, 5), new Vector3(15, 0, 20), levelingFunctionsMesh2x2);
}
}
void AssertMeshLevelPoint(Vector3 testUnleveled, Vector3 controlLeveled, LevelingFunctions levelingFunctions)
{
Vector3 testLeveled = levelingFunctions.GetPositionWithZOffset(testUnleveled);
Assert.AreEqual(testLeveled.X, testUnleveled.X, .001, "We don't adjust the x or y on mesh leveling");
Assert.AreEqual(testLeveled.X, controlLeveled.X, .001, "We don't adjust the x or y on mesh leveling");
Assert.AreEqual(testLeveled.Y, testUnleveled.Y, .001, "We don't adjust the x or y on mesh leveling");
Assert.AreEqual(testLeveled.Y, controlLeveled.Y, .001, "We don't adjust the x or y on mesh leveling");
Assert.AreEqual(testLeveled.Z, controlLeveled.Z, .001);
string outPositionString = levelingFunctions.ApplyLeveling(GetGCodeString(testUnleveled), testUnleveled);
Assert.AreEqual(GetGCodeString(testLeveled), outPositionString);
}
private string GetGCodeString(Vector3 destPosition)
{
return "G1 X{0:0.##} Y{1:0.##} Z{2:0.###}".FormatWith(destPosition.X, destPosition.Y, destPosition.Z);
}
}
}

View file

@ -0,0 +1,139 @@
/*
Copyright (c) 2015, 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.MatterControl.DataStorage;
using MatterHackers.MatterControl.Tests.Automation;
using NUnit.Framework;
namespace MatterHackers.MatterControl.Tests
{
[TestFixture, RunInApplicationDomain]
public class LibraryProviderSqliteTests
{
private bool dataReloaded = false;
private string meshPathAndFileName = TestContext.CurrentContext.ResolveProjectPath(4, "Tests", "TestData", "TestMeshes", "LibraryProviderData", "Box20x20x10.stl");
[SetUp]
public void SetupBeforeTest()
{
dataReloaded = false;
}
#if !__ANDROID__
[Test]
public void LibraryProviderSqlite_NavigationWorking()
{
StaticData.Instance = new FileSystemStaticData(TestContext.CurrentContext.ResolveProjectPath(4, "StaticData"));
MatterControlUtilities.OverrideAppDataLocation(TestContext.CurrentContext.ResolveProjectPath(4));
LibraryProviderSQLite testProvider = new LibraryProviderSQLite(null, null, null, "Local Library");
testProvider.DataReloaded += (sender, e) => { dataReloaded = true; };
Thread.Sleep(3000); // wait for the library to finish initializing
UiThread.InvokePendingActions();
Assert.AreEqual(0, testProvider.CollectionCount, "Start with a new database for these tests.");
Assert.AreEqual(3, testProvider.ItemCount, "Start with a new database for these tests.");
// create a collection and make sure it is on disk
dataReloaded = false; // it has been loaded for the default set of parts
string collectionName = "Collection1";
Assert.IsTrue(!NamedCollectionExists(collectionName)); // assert that the record does not exist in the DB
Assert.IsTrue(dataReloaded == false);
testProvider.AddCollectionToLibrary(collectionName);
Assert.IsTrue(testProvider.CollectionCount == 1);
Assert.IsTrue(dataReloaded == true);
Assert.IsTrue(NamedCollectionExists(collectionName)); // assert that the record does exist in the DB
PrintItemWrapper itemAtRoot = testProvider.GetPrintItemWrapperAsync(0).Result;
// add an item works correctly
dataReloaded = false;
Assert.IsTrue(!NamedItemExists(collectionName));
Assert.IsTrue(dataReloaded == false);
//testProvider.AddFilesToLibrary(new string[] { meshPathAndFileName });
throw new NotImplementedException("testProvider.AddFilesToLibrary(new string[] { meshPathAndFileName });");
Thread.Sleep(3000); // wait for the add to finish
UiThread.InvokePendingActions();
Assert.IsTrue(testProvider.ItemCount == 4);
Assert.IsTrue(dataReloaded == true);
string fileNameWithExtension = Path.GetFileNameWithoutExtension(meshPathAndFileName);
Assert.IsTrue(NamedItemExists(fileNameWithExtension));
// make sure the provider locater is correct
// remove item works
dataReloaded = false;
Assert.IsTrue(dataReloaded == false);
testProvider.RemoveItem(0);
Assert.IsTrue(dataReloaded == true);
Assert.IsTrue(!NamedItemExists(fileNameWithExtension));
// remove collection gets rid of it
dataReloaded = false;
Assert.IsTrue(dataReloaded == false);
testProvider.RemoveCollection(0);
Assert.IsTrue(dataReloaded == true);
Assert.IsTrue(testProvider.CollectionCount == 0);
Assert.IsTrue(!NamedCollectionExists(collectionName)); // assert that the record does not exist in the DB
//MatterControlUtilities.RestoreStaticDataAfterTesting(staticDataState, true);
}
#endif
private bool NamedCollectionExists(string nameToLookFor)
{
string query = string.Format("SELECT * FROM PrintItemCollection WHERE Name = '{0}' ORDER BY Name ASC;", nameToLookFor);
foreach (PrintItemCollection collection in Datastore.Instance.dbSQLite.Query<PrintItemCollection>(query))
{
if (collection.Name == nameToLookFor)
{
return true;
}
}
return false;
}
private bool NamedItemExists(string nameToLookFor)
{
string query = string.Format("SELECT * FROM PrintItem WHERE Name = '{0}' ORDER BY Name ASC;", nameToLookFor);
foreach (PrintItem collection in Datastore.Instance.dbSQLite.Query<PrintItem>(query))
{
if (collection.Name == nameToLookFor)
{
return true;
}
}
return false;
}
}
}

View file

@ -0,0 +1,164 @@
/*
Copyright (c) 2015, 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.PlatformAbstract;
using MatterHackers.MatterControl.DataStorage;
using MatterHackers.MatterControl.PrintLibrary.Provider;
using MatterHackers.MatterControl.PrintQueue;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Threading;
using MatterHackers.MatterControl.Tests.Automation;
using MatterHackers.Agg;
using MatterHackers.GuiAutomation;
namespace MatterControl.Tests
{
[TestFixture]
public class LibraryProviderTests
{
public LibraryProviderTests()
{
#if !__ANDROID__
// Set the static data to point to the directory of MatterControl
StaticData.Instance = new FileSystemStaticData(TestContext.CurrentContext.ResolveProjectPath(4, "StaticData"));
#endif
}
// Timing issues make this test too unstable to run. The DataReloaded event frequently resets the
// dataReloaded variable right after being set to false, resulting in a test failure where dataReloaded is
// asserted to be false but is not. It repros best via command line but does fail in Visual Studio on release
// builds if you run it enough times
[Test]
public void LibraryProviderFileSystem_NavigationWorking()
{
StaticData.Instance = new FileSystemStaticData(TestContext.CurrentContext.ResolveProjectPath(4, "StaticData"));
MatterControlUtilities.OverrideAppDataLocation(TestContext.CurrentContext.ResolveProjectPath(4));
string meshFileName = "Box20x20x10.stl";
string meshPathAndFileName = TestContext.CurrentContext.ResolveProjectPath(5, "MatterControl", "Tests", "TestData", "TestMeshes", "LibraryProviderData", meshFileName);
int dataReloadedCount = 0;
string downloadsDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Downloads");
string testLibraryDirectory = Path.Combine(downloadsDirectory, "LibraryProviderFileSystemTest");
if (Directory.Exists(testLibraryDirectory))
{
Directory.Delete(testLibraryDirectory, true);
}
Directory.CreateDirectory(testLibraryDirectory);
LibraryProviderFileSystem testProvider = new LibraryProviderFileSystem(testLibraryDirectory, "TestPath", null, null);
testProvider.DataReloaded += (s, e) => { dataReloadedCount++; };
AutomationRunner.StaticDelay(() => { return dataReloadedCount > 0; }, 1);
dataReloadedCount = 0;
Assert.IsTrue(testProvider.CollectionCount == 0, "Start with a new database for these tests.");
Assert.IsTrue(testProvider.ItemCount == 0, "Start with a new database for these tests.");
// create a collection and make sure it is on disk
Assert.AreEqual(0, dataReloadedCount); // it has been loaded for the default set of parts
string collectionName = "Collection1";
string createdDirectory = Path.Combine(testLibraryDirectory, collectionName);
Assert.IsFalse(Directory.Exists(createdDirectory), "CreatedDirectory should *not* exist");
Assert.AreEqual(0, dataReloadedCount, "Reload should *not* have occurred");
testProvider.AddCollectionToLibrary(collectionName);
AutomationRunner.StaticDelay(() => { return testProvider.CollectionCount == 1; }, 1);
Assert.AreEqual(1, testProvider.CollectionCount, "Incorrect collection count");
Assert.IsTrue(dataReloadedCount > 0, "Reload should *have* occurred");
Assert.IsTrue(Directory.Exists(createdDirectory), "CreatedDirectory *should* exist");
// add an item works correctly
LibraryProvider subProvider = testProvider.GetProviderForCollection(testProvider.GetCollectionItem(0));
subProvider.DataReloaded += (sender, e) => { dataReloadedCount++; };
dataReloadedCount = 0;
string subPathAndFile = Path.Combine(createdDirectory, meshFileName);
Assert.IsFalse(File.Exists(subPathAndFile), "File should *not* exist: " + subPathAndFile);
Assert.AreEqual(0, dataReloadedCount, "Reload should *not* have occurred");
// WIP: saving the name incorrectly for this location (does not need to be changed).
//subProvider.AddFilesToLibrary(new string[] { meshPathAndFileName });
throw new NotImplementedException("subProvider.AddFilesToLibrary(new string[] { meshPathAndFileName });");
AutomationRunner.StaticDelay(() => { return subProvider.ItemCount == 1; }, 1);
PrintItemWrapper itemAtRoot = subProvider.GetPrintItemWrapperAsync(0).Result;
Assert.IsTrue(subProvider.ItemCount == 1);
Assert.IsTrue(dataReloadedCount > 0);
//Assert.IsTrue(itemAdded == true);
Assert.IsTrue(File.Exists(subPathAndFile));
// make sure the provider locater is correct
// remove item works
dataReloadedCount = 0;
Assert.IsTrue(dataReloadedCount == 0);
subProvider.RemoveItem(0);
AutomationRunner.StaticDelay(() => { return subProvider.ItemCount == 0; }, 1);
Assert.IsTrue(dataReloadedCount > 0);
Assert.IsTrue(!File.Exists(subPathAndFile));
// remove collection gets rid of it
dataReloadedCount = 0;
testProvider.RemoveCollection(0);
AutomationRunner.StaticDelay(() => { return testProvider.CollectionCount == 0; }, 1);
Assert.IsTrue(dataReloadedCount > 0);
Assert.IsTrue(testProvider.CollectionCount == 0);
Assert.IsTrue(!Directory.Exists(createdDirectory));
if (Directory.Exists(testLibraryDirectory))
{
Directory.Delete(testLibraryDirectory, true);
}
}
private bool NamedCollectionExists(string nameToLookFor)
{
string query = string.Format("SELECT * FROM PrintItemCollection WHERE Name = '{0}' ORDER BY Name ASC;", nameToLookFor);
foreach (PrintItemCollection collection in Datastore.Instance.dbSQLite.Query<PrintItemCollection>(query))
{
if (collection.Name == nameToLookFor)
{
return true;
}
}
return false;
}
}
}

View file

@ -0,0 +1,113 @@
/*
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.IO;
using System.Reflection;
using MatterHackers.Agg;
using MatterHackers.Agg.Image;
using MatterHackers.Agg.UI;
using MatterHackers.MatterControl.Tests.Automation;
using NUnit.Framework;
namespace MatterHackers.MatterControl.UI
{
[TestFixture, Category("MatterControl.UI")]
public class MatterControlUiFeatures
{
private static bool saveImagesForDebug = true;
/// <summary>
/// Bug #94150618 - Left margin is not applied on GuiWidget
/// </summary>
[Test]
public void TopToBottomContainerAppliesExpectedMarginToToggleView()
{
TestContext.CurrentContext.SetCompatibleWorkingDirectory();
int marginSize = 40;
int dimensions = 300;
GuiWidget outerContainer = new GuiWidget(dimensions, dimensions);
FlowLayoutWidget topToBottomContainer = new FlowLayoutWidget(FlowDirection.TopToBottom)
{
HAnchor = HAnchor.Stretch,
VAnchor = VAnchor.Stretch,
};
outerContainer.AddChild(topToBottomContainer);
CheckBox toggleBox = new CheckBox("test");
toggleBox.HAnchor = HAnchor.Stretch;
toggleBox.VAnchor = VAnchor.Stretch;
toggleBox.Margin = new BorderDouble(marginSize);
toggleBox.BackgroundColor = Color.Red;
toggleBox.DebugShowBounds = true;
topToBottomContainer.AddChild(toggleBox);
topToBottomContainer.AnchorAll();
topToBottomContainer.PerformLayout();
outerContainer.DoubleBuffer = true;
outerContainer.BackBuffer.NewGraphics2D().Clear(Color.White);
outerContainer.OnDraw(outerContainer.NewGraphics2D());
// For troubleshooting or visual validation
OutputImages(outerContainer, outerContainer);
var bounds = toggleBox.BoundsRelativeToParent;
Assert.IsTrue(bounds.Left == marginSize, "Left margin is incorrect");
Assert.IsTrue(bounds.Right == dimensions - marginSize, "Right margin is incorrect");
Assert.IsTrue(bounds.Top == dimensions - marginSize, "Top margin is incorrect");
Assert.IsTrue(bounds.Bottom == marginSize, "Bottom margin is incorrect");
}
private static void OutputImage(ImageBuffer imageToOutput, string fileName)
{
if (saveImagesForDebug)
{
ImageTgaIO.Save(imageToOutput, fileName);
}
}
private static void OutputImage(GuiWidget widgetToOutput, string fileName)
{
if (saveImagesForDebug)
{
OutputImage(widgetToOutput.BackBuffer, fileName);
}
}
private static void OutputImages(GuiWidget control, GuiWidget test)
{
OutputImage(control, "image-control.tga");
OutputImage(test, "image-test.tga");
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,121 @@
/*
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.
*/
#define DEBUG_INTO_TGAS
using System.Linq;
using System.Threading.Tasks;
using MatterHackers.Agg.Platform;
using MatterHackers.Agg.UI;
using MatterHackers.DataConverters3D;
using MatterHackers.MatterControl;
using MatterHackers.MatterControl.DesignTools;
using MatterHackers.MatterControl.Tests.Automation;
using NUnit.Framework;
namespace MatterHackers.PolygonMesh.UnitTests
{
[TestFixture, Category("Agg.PolygonMesh.Rebuild")]
public class MeshRebuildTests
{
public void SetupEvnironment()
{
StaticData.RootPath = MatterControlUtilities.StaticDataPath;
MatterControlUtilities.OverrideAppDataLocation(MatterControlUtilities.RootPath);
// Automation runner must do as much as program.cs to spin up platform
string platformFeaturesProvider = "MatterHackers.MatterControl.WindowsPlatformsFeatures, MatterControl.Winforms";
AppContext.Platform = AggContext.CreateInstanceFrom<INativePlatformFeatures>(platformFeaturesProvider);
AppContext.Platform.InitPluginFinder();
AppContext.Platform.ProcessCommandline();
}
[Test]
public async Task PinchChangesMesh()
{
SetupEvnironment();
var root = new Object3D();
// now add a pinch
var pinch1 = new PinchObject3D_3();
pinch1.Children.Add(new CubeObject3D());
await pinch1.Rebuild();
root.Children.Add(pinch1);
Assert.AreEqual(3, root.Descendants().Count());
}
}
[TestFixture, Category("Agg.Scene.Rebuild")]
public class SceenSheetTests
{
public void SetupEvnironment()
{
StaticData.RootPath = MatterControlUtilities.StaticDataPath;
MatterControlUtilities.OverrideAppDataLocation(MatterControlUtilities.RootPath);
// Automation runner must do as much as program.cs to spin up platform
string platformFeaturesProvider = "MatterHackers.MatterControl.WindowsPlatformsFeatures, MatterControl.Winforms";
AppContext.Platform = AggContext.CreateInstanceFrom<INativePlatformFeatures>(platformFeaturesProvider);
AppContext.Platform.InitPluginFinder();
AppContext.Platform.ProcessCommandline();
}
[Test]
public async Task TestSheetFunctions()
{
SetupEvnironment();
var root = new Object3D();
// Create the scene (a cube and a sheet)
var cube1 = new CubeObject3D();
root.Children.Add(cube1);
var sheet = await SheetObject3D.Create();
root.Children.Add(sheet);
// set the sheet A1 to 33
sheet.SheetData[0, 0].Expression = "=33";
// rebuild cube without a reference to sheet
cube1.Invalidate(InvalidateType.Properties);
Assert.AreEqual(20, cube1.Width.Value(cube1), "cube1 should be the default 20mm");
// set the cube width to the sheet value, but with a bad description (needs an equals to work)
cube1.Width = "A1";
cube1.Invalidate(InvalidateType.Properties);
Assert.AreEqual(CubeObject3D.MinEdgeSize, cube1.Width.Value(cube1), "Should be the minimum cube value as the reference is bad");
// now fix the reference
cube1.Width = "=A1";
cube1.Invalidate(InvalidateType.Properties);
Assert.AreEqual(33, cube1.Width.Value(cube1), "Should now be the value ad A1");
// Change the sheet value
sheet.SheetData[0, 0].Expression = "=43";
sheet.SheetData.Recalculate();
// and rebuild the references
sheet.Invalidate(InvalidateType.SheetUpdated);
Assert.AreEqual(43, cube1.Width.Value(cube1));
}
}
}

View file

@ -0,0 +1,835 @@
/*
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;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using MatterHackers.Agg.Platform;
using MatterHackers.MatterControl.SlicerConfiguration;
using MatterHackers.MatterControl.Tests.Automation;
using NUnit.Framework;
using TestInvoker;
namespace MatterControl.Tests.MatterControl
{
[TestFixture, Category("OemProfiles")]
public class OemProfileTests
{
private static List<PrinterTestDetails> allPrinters;
private static string printerSettingsDirectory = Path.Combine(MatterControlUtilities.StaticDataPath, "Profiles");
private string pauseGCode = @"M76 ; pause print timer
G91
G1 Z10 E-5.0 F1800
G90
G1 X5 F[travel_speed]
M300 S3000 P30 ; Pause Tone
M300 S1500 P30 ; Pause Tone
M300 S3000 P30 ; Pause Tone
M300 S1500 P30 ; Pause Tone
M300 S3000 P30 ; Pause Tone
M300 S1500 P30 ; Pause Tone
M300 S3000 P30 ; Pause Tone
M300 S1500 P30 ; Pause Tone
M300 S750 P30 ; Pause Tone
M300 S1500 P30 ; Pause Tone
M300 S750 P30 ; Pause Tone
M300 S1500 P30 ; Pause Tone
M300 S750 P30 ; Pause Tone
M300 S1500 P30 ; Pause Tone
M300 S750 P30 ; Pause Tone";
private string resumeGCode = @"M75 ; Start the print job timer
G91
G1 Z-10 F1800
G90
M300 S750 P30 ; Resume Tone
M300 S1500 P30 ; Resume Tone
M300 S750 P30 ; Resume Tone
M300 S1500 P30 ; Resume Tone
M300 S750 P30 ; Resume Tone
M300 S1500 P30 ; Resume Tone
M300 S750 P30 ; Resume Tone
M300 S1500 P30 ; Resume Tone
M300 S3000 P30 ; Resume Tone
M300 S1500 P30 ; Resume Tone
M300 S3000 P30 ; Resume Tone
M300 S1500 P30 ; Resume Tone
M300 S3000 P30 ; Resume Tone
M300 S1500 P30 ; Resume Tone
M300 S3000 P30 ; Resume Tone";
static OemProfileTests()
{
StaticData.RootPath = MatterControlUtilities.StaticDataPath;
MatterControlUtilities.OverrideAppDataLocation(MatterControlUtilities.RootPath);
allPrinters = (from printerFile in new DirectoryInfo(printerSettingsDirectory).GetFiles("*.printer", SearchOption.AllDirectories)
select new PrinterTestDetails
{
PrinterName = printerFile.Name,
Oem = printerFile.Directory.Name,
ConfigPath = printerFile.FullName,
RelativeFilePath = printerFile.FullName.Substring(printerSettingsDirectory.Length + 1),
PrinterSettings = PrinterSettings.LoadFile(printerFile.FullName)
}).ToList();
}
[Test, ChildProcessTest]
public void ModifyPulsePrinterProfilesSettings()
{
// This is not really a test. It updaets our profiles with new settings.
return;
StaticData.RootPath = MatterControlUtilities.StaticDataPath;
MatterControlUtilities.OverrideAppDataLocation(MatterControlUtilities.RootPath);
string profilePath = @"C:\\Users\\LarsBrubaker\\Downloads\\Pulse E Profiles";
allPrinters = (from printerFile in new DirectoryInfo(profilePath).GetFiles("*.printer", SearchOption.TopDirectoryOnly)
select new PrinterTestDetails
{
PrinterName = printerFile.Name,
Oem = printerFile.Directory.Name,
ConfigPath = printerFile.FullName,
RelativeFilePath = printerFile.FullName.Substring(printerSettingsDirectory.Length + 1),
PrinterSettings = PrinterSettings.LoadFile(printerFile.FullName)
}).ToList();
string ConvertString(string input)
{
return input.Replace("\n", "\\n").Replace("\r", "");
}
void ChangeSettings(PrinterSettings printerSettings)
{
var printerModel = printerSettings.GetValue(SettingsKey.model);
// general
printerSettings.SetValue(SettingsKey.fill_density, "30%");
printerSettings.SetValue(SettingsKey.avoid_crossing_perimeters, "1");
printerSettings.SetValue(SettingsKey.merge_overlapping_lines, "1");
printerSettings.SetValue(SettingsKey.seam_placement, "Centered In Back");
printerSettings.SetValue(SettingsKey.expand_thin_walls, "1");
printerSettings.SetValue(SettingsKey.coast_at_end_distance, "0");
printerSettings.SetValue(SettingsKey.monotonic_solid_infill, "1");
printerSettings.SetValue(SettingsKey.infill_overlap_perimeter, "20%");
printerSettings.SetValue(SettingsKey.avoid_crossing_max_ratio, "3");
printerSettings.SetValue(SettingsKey.perimeter_start_end_overlap, "35");
// speed
printerSettings.SetValue(SettingsKey.external_perimeter_speed, "25");
printerSettings.SetValue(SettingsKey.perimeter_acceleration, "800");
printerSettings.SetValue(SettingsKey.default_acceleration, "1300");
printerSettings.SetValue(SettingsKey.bridge_over_infill, "1");
// adheasion
printerSettings.SetValue(SettingsKey.create_skirt, "1");
// support
printerSettings.SetValue(SettingsKey.retract_lift, ".6");
printerSettings.SetValue(SettingsKey.min_extrusion_before_retract, "0");
printerSettings.SetValue(SettingsKey.retract_before_travel_avoid, "20");
// printer gcode settings
printerSettings.SetValue(SettingsKey.pause_gcode, ConvertString(pauseGCode));
printerSettings.SetValue(SettingsKey.resume_gcode, ConvertString(resumeGCode));
printerSettings.SetValue(SettingsKey.support_material_create_perimeter, "1");
// e series settings
if (printerModel.Contains('E'))
{
// clear all material settings
printerSettings.MaterialLayers = new List<PrinterSettingsLayer>();
printerSettings.ActiveMaterialKey = "";
printerSettings.SetValue(SettingsKey.has_swappable_bed, "1");
printerSettings.SetValue(SettingsKey.bed_temperature_buildtak, "55");
printerSettings.SetValue(SettingsKey.bed_temperature_garolite, "75");
printerSettings.SetValue(SettingsKey.bed_temperature_glass, "75");
printerSettings.SetValue(SettingsKey.bed_temperature_kapton, "55");
printerSettings.SetValue(SettingsKey.bed_temperature_pei, "75");
printerSettings.SetValue(SettingsKey.bed_temperature_pp, "55");
var zHeight = 215;
// check the build height based on the extruder type
switch (printerModel[3])
{
case '3': // Volcano
case '6': // LGX E3Dv6
zHeight = 205;
break;
}
printerSettings.SetValue(SettingsKey.build_height, zHeight.ToString());
// make sure the start gcode travels fast to priming line
var startGCode = printerSettings.GetValue(SettingsKey.start_gcode);
startGCode = startGCode.Replace("G1 Y5 X5 Z0.8 F1800 ; Purge line", "G1 Y5 X5 [travel_speed] ; Purge line\\nG1 Z0.8 F1800");
printerSettings.SetValue(SettingsKey.start_gcode, startGCode);
}
// 32 bit settings
if (printerModel.Contains('M') || printerModel.Contains('S'))
{
// If the board is 32 bit we cannot update the firmware.
// make sure it does not show a firmware updater
printerSettings.SetValue(SettingsKey.include_firmware_updater, "None");
}
}
foreach (var printer in allPrinters)
{
ChangeSettings(printer.PrinterSettings);
printer.PrinterSettings.Save(Path.Combine(Path.GetDirectoryName(printer.ConfigPath), "output", printer.PrinterName), true);
}
int a = 0;
}
[Test, ChildProcessTest]
public void AllMaterialsLibraryHasGoodProfiles()
{
var materialSettingsDirectory = Path.Combine(MatterControlUtilities.StaticDataPath, "Materials");
var directoryInfo = new DirectoryInfo(materialSettingsDirectory);
var files = directoryInfo.GetFiles("*.material", SearchOption.AllDirectories);
var profiles = files.Select(f => PrinterSettings.LoadFile(f.FullName)).ToList();
var allMaterialIds = new HashSet<string>();
var notPresentKeys = new string[]
{
"brims",
"brims_layers",
"coast_at_end_distance",
"create_brim",
"create_skirt",
"expand_thin_walls",
"extruder_count",
"extrusion_multiplier",
"extrusion_ratio",
"filament_diameter",
"fill_angle",
// "fill_density", // BASF uses this
"fill_pattern",
"fill_thin_gaps",
"first_layer_extrusion_width",
"first_layer_height",
"layer_height",
"monotonic_solid_infill"
};
var isPresentKeys = new string[]
{
"bed_temperature_blue_tape",
"bed_temperature_buildtak",
"bed_temperature_garolite",
"bed_temperature_glass",
"bed_temperature_kapton",
"bed_temperature_pei",
"bed_temperature_pp",
"filament_density",
"layer_id",
"layer_name",
"material_sku",
"temperature",
};
for(var i = 0; i < profiles.Count; i++)
{
var profile = profiles[i];
Assert.AreEqual(1, profile.MaterialLayers.Count, "Each material profile should have 1 material in it");
var material = profile.MaterialLayers[0];
profile.ActiveMaterialKey = material.LayerID;
Assert.IsTrue(!string.IsNullOrEmpty(profile.GetValue(SettingsKey.material_sku)), $"All profiles should have a material_sku set {files[i].FullName}");
Assert.IsTrue(!allMaterialIds.Contains(material.LayerID), $"Every material needs a unique Id {files[i].FullName}");
foreach(var key in isPresentKeys)
{
Assert.IsTrue(material.ContainsKey(key), $"Material {files[i].FullName} should include {key} setting");
}
if (profile.GetValue(SettingsKey.layer_name).ToLower().Contains("nylon"))
{
// make sure the setting for garolite is greater than 0 and not NC
double.TryParse(profile.GetValue(SettingsKey.bed_temperature_garolite), out double temp);
Assert.Greater(temp, 0);
}
foreach (var key in notPresentKeys)
{
Assert.IsTrue(!material.ContainsKey(key), $"Material {files[i].FullName} should not include {key} setting");
}
allMaterialIds.Add(material.LayerID);
}
}
[Test]
public void LayerGCodeHasExpectedValue()
{
// Verifies "layer_gcode" is expected value: "; LAYER:[layer_num]"
ValidateOnAllPrinters((printer, settings, settingsType) =>
{
if (settings.GetValue(SettingsKey.layer_gcode) != "; LAYER:[layer_num]")
{
printer.RuleViolated = true;
// SetSettingInOem(printer, settings, SettingsKey.layer_gcode, "; LAYER:[layer_num]");
}
});
}
private static void SetSettingInOem(PrinterTestDetails printer, PrinterSettings settings, string key, string value)
{
// Fix existing invalid items...
string layerValue;
if (settings.OemLayer.TryGetValue(key, out layerValue) && layerValue == "")
{
settings.OemLayer.Remove(key);
}
if (settings.QualityLayer?.TryGetValue(key, out layerValue) == true && layerValue == "")
{
settings.QualityLayer.Remove(key);
}
if (settings.MaterialLayer?.TryGetValue(key, out layerValue) == true && layerValue == "")
{
settings.MaterialLayer.Remove(key);
}
settings.OemLayer[key] = value;
// Reset to default values
settings.UserLayer.Remove(SettingsKey.active_quality_key);
settings.UserLayer.Remove(SettingsKey.active_material_key);
settings.StagedUserSettings = new PrinterSettingsLayer();
settings.Save(printer.ConfigPath);
}
[Test]
public void StartGCodeWithExtrudesMustFollowM109Heatup()
{
ValidateOnAllPrinters((printer, settings, settingsType) =>
{
// Get the start_gcode string
string startGcode = settings.OemLayer.ValueOrDefault(SettingsKey.start_gcode) ?? string.Empty;
// Only validate start_gcode configs that have M109 and extrude statements
if (startGcode.Contains("M109") && startGcode.Contains("G1 E"))
{
// Split start_gcode on newlines
var lines = startGcode.Split(new string[] { "\\n" }, StringSplitOptions.RemoveEmptyEntries).Select(l => l.ToUpper().Trim()).ToList();
// Find first instance of M109 or 'G1 E' extrude
string m109Line = lines.Where(l => l.StartsWith("M109 ")).FirstOrDefault();
string extrudeLine = lines.Where(l => l.StartsWith("G1 E")).FirstOrDefault();
if (m109Line == null)
{
printer.RuleViolated = true;
return;
}
int m109Pos = lines.IndexOf(m109Line);
int extrudePos = lines.IndexOf(extrudeLine);
Assert.IsNotNull(m109Line);
// Assert.IsNotNull(emptyExtrudeLine);
// Assert.Greater(emptyExtrudePos, m109Pos);
if (extrudePos < m109Pos)
{
printer.RuleViolated = true;
}
}
});
}
[Test]
public void CsvBedSizeExistsAndHasTwoValues()
{
ValidateOnAllPrinters((printer, settings, settingsType) =>
{
// Bed size is not required in slice files
if (printer.RelativeFilePath.IndexOf(".slice", StringComparison.OrdinalIgnoreCase) != -1)
{
return;
}
string bedSize = settings.GetValue(SettingsKey.bed_size);
// Must exist in all configs
Assert.IsTrue(!string.IsNullOrEmpty(bedSize), "[bed_size] must exist: " + printer.RelativeFilePath);
string[] segments = bedSize.Trim().Split(',');
// Must be a CSV and have two values
Assert.AreEqual(2, segments.Length, "[bed_size] should have two values separated by a comma: " + printer.RelativeFilePath);
});
}
[Test]
public void CsvPrintCenterExistsAndHasTwoValues()
{
ValidateOnAllPrinters((printer, settings, settingsType) =>
{
// Printer center is not required in slice files
if (printer.RelativeFilePath.IndexOf(".slice", StringComparison.OrdinalIgnoreCase) != -1)
{
return;
}
string printCenter = settings.GetValue(SettingsKey.print_center);
// Must exist in all configs
Assert.IsTrue(!string.IsNullOrEmpty(printCenter), "[print_center] must exist: " + printer.RelativeFilePath);
string[] segments = printCenter.Trim().Split(',');
// Must be a CSV and have only two values
Assert.AreEqual(2, segments.Length, "[print_center] should have two values separated by a comma: " + printer.RelativeFilePath);
#if false
// save out the material settings
foreach(var materialLayer in settings.MaterialLayers)
{
// create an empyt profile
var materialSettings = new PrinterSettings();
// copy just this material setting to it
materialSettings.MaterialLayers.Add(materialLayer.Clone());
// save it
var fileName = ApplicationController.Instance.SanitizeFileName(materialLayer.Name);
if (!string.IsNullOrEmpty(fileName))
{
fileName = Path.Combine(@"C:\temp", "materials", fileName) + ".material";
File.WriteAllText(fileName, JsonConvert.SerializeObject(materialSettings, Formatting.Indented));
}
}
#endif
});
}
[Test]
public void RetractLengthIsLessThanTwenty()
{
ValidateOnAllPrinters((printer, settings, settingsType) =>
{
string retractLengthString = settings.GetValue(SettingsKey.retract_length);
if (!string.IsNullOrEmpty(retractLengthString))
{
float retractLength;
if (!float.TryParse(retractLengthString, out retractLength))
{
Assert.Fail("Invalid [retract_length] value (float parse failed): " + printer.RelativeFilePath);
}
Assert.Less(retractLength, 20, "[retract_length]: " + printer.RelativeFilePath);
}
});
}
[Test]
public void ExtruderCountIsGreaterThanZero()
{
ValidateOnAllPrinters((printer, settings, settingsType) =>
{
string extruderCountString = settings.GetValue("extruder_count");
if (!string.IsNullOrEmpty(extruderCountString))
{
int extruderCount;
if (!int.TryParse(extruderCountString, out extruderCount))
{
Assert.Fail("Invalid [extruder_count] value (int parse failed): " + printer.RelativeFilePath);
}
// Must be greater than zero
Assert.Greater(extruderCount, 0, "[extruder_count]: " + printer.RelativeFilePath);
}
});
}
[Test]
public void MinFanSpeedOneHundredOrLess()
{
ValidateOnAllPrinters((printer, settings, settingsType) =>
{
string fanSpeedString = settings.GetValue(SettingsKey.min_fan_speed);
if (!string.IsNullOrEmpty(fanSpeedString))
{
// Must be valid int data
int minFanSpeed;
if (!int.TryParse(fanSpeedString, out minFanSpeed))
{
Assert.Fail("Invalid [min_fan_speed] value (int parse failed): " + printer.RelativeFilePath);
}
// Must be less than or equal to 100
Assert.LessOrEqual(minFanSpeed, 100, "[min_fan_speed]: " + printer.RelativeFilePath);
}
});
}
[Test]
public void PlaAndAbsDensitySetCorrectly()
{
ValidateOnAllPrinters((printer, settings, settingsType) =>
{
if (settings.OemLayer.ContainsKey(SettingsKey.layer_name))
{
if (settings.OemLayer[SettingsKey.layer_name].ToUpper() == "ABS")
{
double absDensity = settings.GetValue<double>(SettingsKey.filament_density);
if (absDensity != 1.04)
{
Assert.Fail("[filament_density] value should be set to ABS 1.04: " + printer.RelativeFilePath);
}
}
else if (settings.OemLayer[SettingsKey.layer_name].ToUpper() == "PLA")
{
double absDensity = settings.GetValue<double>(SettingsKey.filament_density);
if (absDensity != 1.24)
{
Assert.Fail("[filament_density] value should be set to PLA 1.24: " + printer.RelativeFilePath);
}
}
}
});
}
[Test]
public void MaxFanSpeedOneHundredOrLess()
{
ValidateOnAllPrinters((printer, settings, settingsType) =>
{
string fanSpeedString = settings.GetValue(SettingsKey.max_fan_speed);
if (!string.IsNullOrEmpty(fanSpeedString))
{
// Must be valid int data
int maxFanSpeed;
if (!int.TryParse(fanSpeedString, out maxFanSpeed))
{
Assert.Fail("Invalid [max_fan_speed] value (int parse failed): " + printer.RelativeFilePath);
}
// Must be less than or equal to 100
Assert.LessOrEqual(maxFanSpeed, 100, "[max_fan_speed]: " + printer.RelativeFilePath);
}
});
}
[Test]
public void NoCurlyBracketsInGcode()
{
ValidateOnAllPrinters((printer, settings, settingsType) =>
{
// TODO: Why aren't we testing all gcode sections?
string[] keysToTest = { SettingsKey.start_gcode, SettingsKey.end_gcode };
foreach (string gcodeKey in keysToTest)
{
string gcode = settings.GetValue(gcodeKey);
if (gcode.Contains("{") || gcode.Contains("}"))
{
Assert.Fail(string.Format("[{0}] Curly brackets not allowed: {1}", gcodeKey, printer.RelativeFilePath));
}
}
});
}
[Test]
public void BottomSolidLayersNotZero()
{
ValidateOnAllPrinters((printer, settings, settingsType) =>
{
string bottomSolidLayers = settings.GetValue(SettingsKey.bottom_solid_layers);
if (!string.IsNullOrEmpty(bottomSolidLayers))
{
if (bottomSolidLayers == "0")
{
printer.RuleViolated = true;
return;
}
// Assert.AreEqual("1mm", bottomSolidLayers, "[bottom_solid_layers] must be 1mm: " + printer.RelativeFilePath);
}
});
}
[Test]
public void NoFirstLayerBedTempInStartGcode()
{
ValidateOnAllPrinters((printer, settings, settingsType) =>
{
string startGcode = settings.GetValue(SettingsKey.start_gcode);
Assert.False(startGcode.Contains(SettingsKey.first_layer_bed_temperature), "[start_gcode] should not contain [first_layer_bed_temperature]" + printer.RelativeFilePath);
});
}
[Test]
public void FirstLayerHeightLessThanNozzleDiameterXExtrusionMultiplier()
{
ValidateOnAllPrinters((printer, settings, settingsType) =>
{
if (settings.GetValue(SettingsKey.output_only_first_layer) == "1")
{
return;
}
float nozzleDiameter = float.Parse(settings.GetValue(SettingsKey.nozzle_diameter));
float layerHeight = float.Parse(settings.GetValue(SettingsKey.layer_height));
float firstLayerExtrusionWidth;
string firstLayerExtrusionWidthString = settings.GetValue(SettingsKey.first_layer_extrusion_width);
if (!string.IsNullOrEmpty(firstLayerExtrusionWidthString) && firstLayerExtrusionWidthString.Trim() != "0")
{
firstLayerExtrusionWidth = ValueOrPercentageOf(firstLayerExtrusionWidthString, nozzleDiameter);
}
else
{
firstLayerExtrusionWidth = nozzleDiameter;
}
string firstLayerHeightString = settings.GetValue(SettingsKey.first_layer_height);
if (!string.IsNullOrEmpty(firstLayerHeightString))
{
float firstLayerHeight = ValueOrPercentageOf(firstLayerHeightString, layerHeight);
if (firstLayerHeight > firstLayerExtrusionWidth)
{
printer.RuleViolated = true;
return;
}
}
});
}
[Test]
public void LayerHeightLessThanNozzleDiameter()
{
ValidateOnAllPrinters((printer, settings, settingsType) =>
{
if (settings.GetValue(SettingsKey.output_only_first_layer) == "1")
{
return;
}
float nozzleDiameter = float.Parse(settings.GetValue(SettingsKey.nozzle_diameter));
float layerHeight = float.Parse(settings.GetValue(SettingsKey.layer_height));
double maximumLayerHeight = nozzleDiameter * 85;
// TODO: Remove once validated and resolved
if (layerHeight >= maximumLayerHeight)
{
printer.RuleViolated = true;
return;
}
Assert.Less(layerHeight, maximumLayerHeight, "[layer_height] must be less than [minimumLayerHeight]: " + printer.RelativeFilePath);
});
}
[Test]
public void FirstLayerExtrusionWidthGreaterThanNozzleDiameterIfSet()
{
ValidateOnAllPrinters((printer, settings, settingsType) =>
{
float nozzleDiameter = float.Parse(settings.GetValue(SettingsKey.nozzle_diameter));
string firstLayerExtrusionWidthString = settings.GetValue(SettingsKey.first_layer_extrusion_width);
if (!string.IsNullOrEmpty(firstLayerExtrusionWidthString))
{
float firstLayerExtrusionWidth = ValueOrPercentageOf(firstLayerExtrusionWidthString, nozzleDiameter);
if (firstLayerExtrusionWidth == 0)
{
// Ignore zeros
return;
}
Assert.GreaterOrEqual(firstLayerExtrusionWidth, nozzleDiameter, "[first_layer_extrusion_width] must be nozzle diameter or greater: " + printer.RelativeFilePath);
}
});
}
[Test]
public void SupportMaterialAssignedToExtruderOne()
{
ValidateOnAllPrinters((printer, settings, settingsType) =>
{
var supportMaterialExtruder = settings.GetValue<int>(SettingsKey.support_material_extruder);
var extruderCount = settings.GetValue<int>(SettingsKey.extruder_count);
// the support extruder should be 0 unless you are on a material setting
if (supportMaterialExtruder <= 0
|| (settingsType == SettingsType.Material && extruderCount > 1)
|| (settingsType == SettingsType.Quality && extruderCount > 1))
{
// this is a valid printer profile
}
else
{
// this needs to be fixed
printer.RuleViolated = true;
}
});
}
[Test]
public void SupportInterfaceMaterialAssignedToExtruderOne()
{
ValidateOnAllPrinters((printer, settings, settingsType) =>
{
var supportMaterialInterfaceExtruder = settings.GetValue<int>(SettingsKey.support_material_interface_extruder);
var extruderCount = settings.GetValue<int>(SettingsKey.extruder_count);
// the support extruder should be 0 unless you are on a material setting
if (supportMaterialInterfaceExtruder <= 0
|| (settingsType == SettingsType.Material && extruderCount > 1)
|| (settingsType == SettingsType.Quality && extruderCount > 1))
{
// this is a valid printer profile
}
else
{
// this needs to be fixed
printer.RuleViolated = true;
}
});
}
private static float ValueOrPercentageOf(string valueOrPercent, float baseValue)
{
if (valueOrPercent.Contains("%"))
{
float percentage = float.Parse(valueOrPercent.Replace("%", "")) / 100;
return baseValue * percentage;
}
else
{
return float.Parse(valueOrPercent);
}
}
public enum SettingsType
{
All,
Material,
Quality
}
/// <summary>
/// Calls the given delegate for each printer as well as each quality/material layer, passing in a PrinterConfig object that has
/// printer settings loaded into a SettingsLayer as well as state about the printer
/// </summary>
/// <param name="action">The action to invoke for each printer</param>
private void ValidateOnAllPrinters(Action<PrinterTestDetails, PrinterSettings, SettingsType> action)
{
var ruleViolations = new List<string>();
foreach (var printer in allPrinters)
{
printer.RuleViolated = false;
var printerSettings = printer.PrinterSettings;
printerSettings.AutoSave = false;
// Disable active material/quality overrides
printerSettings.ActiveMaterialKey = "";
printerSettings.ActiveQualityKey = "";
// Validate just the OemLayer
action(printer, printerSettings, SettingsType.All);
if (printer.RuleViolated)
{
ruleViolations.Add(printer.RelativeFilePath);
}
// Validate material layers
foreach (var layer in printer.PrinterSettings.MaterialLayers)
{
printer.RuleViolated = false;
printerSettings.ActiveMaterialKey = layer.LayerID;
// Validate the settings with this material layer active
action(printer, printerSettings, SettingsType.Material);
if (printer.RuleViolated)
{
ruleViolations.Add(printer.RelativeFilePath + " -> " + layer.Name);
}
}
printerSettings.ActiveMaterialKey = "";
// Validate quality layers
foreach (var layer in printer.PrinterSettings.QualityLayers)
{
printer.RuleViolated = false;
printerSettings.ActiveQualityKey = layer.LayerID;
// Validate the settings with this quality layer active
action(printer, printerSettings, SettingsType.Quality);
if (printer.RuleViolated)
{
ruleViolations.Add(printer.RelativeFilePath + " -> " + layer.Name);
}
}
}
Assert.IsTrue(
ruleViolations.Count == 0, /* Use == instead of Assert.AreEqual to better convey failure details */
string.Format("One or more printers violate this rule: \r\n\r\n{0}\r\n", string.Join("\r\n", ruleViolations.ToArray())));
}
private class PrinterTestDetails
{
public string PrinterName { get; set; }
public string Oem { get; set; }
public string ConfigPath { get; set; }
public string RelativeFilePath { get; set; }
public PrinterSettings PrinterSettings { get; set; }
// HACK: short term hack to support a general purpose test rollup function for cases where multiple config files
// violate a rule and in the short term we want to report and resolve the issues in batch rather than having a
// single test failure. Long term the single test failure better communicates the issue and assist with troubleshooting
// by using .AreEqual .LessOrEqual, etc. to communicate intent
public bool RuleViolated { get; set; } = false;
}
}
}

View file

@ -0,0 +1,91 @@
/*
Copyright (c) 2018, 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;
using MatterHackers.Agg.Platform;
using MatterHackers.MatterControl;
using MatterHackers.MatterControl.DataStorage;
using MatterHackers.MatterControl.Tests.Automation;
using NUnit.Framework;
using TestInvoker;
namespace MatterControl.Tests.MatterControl
{
[TestFixture, Parallelizable(ParallelScope.Children)]
public class PathTests
{
[Test, ChildProcessTest]
public Task CacheablePathTest()
{
StaticData.RootPath = StaticData.RootPath = MatterControlUtilities.StaticDataPath;
string path = ApplicationController.CacheablePath("scope", "key.file");
Assert.AreEqual(
path.Substring(path.IndexOf("MatterControl")),
Path.Combine("MatterControl", "data", "temp", "cache", "scope", "key.file"),
"Unexpected CacheablePath Value");
return Task.CompletedTask;
}
[Test, ChildProcessTest]
public Task CacheDirectoryTest()
{
StaticData.RootPath = MatterControlUtilities.StaticDataPath;
string path = ApplicationDataStorage.Instance.CacheDirectory;
Assert.AreEqual(
path.Substring(path.IndexOf("MatterControl")),
Path.Combine("MatterControl", "data", "temp", "cache"),
"Unexpected CacheDirectory Value");
return Task.CompletedTask;
}
[Test, ChildProcessTest]
public Task TempPathTest()
{
StaticData.RootPath = MatterControlUtilities.StaticDataPath;
string path = ApplicationDataStorage.Instance.ApplicationTempDataPath;
Assert.AreEqual(
path.Substring(path.IndexOf("MatterControl")),
Path.Combine("MatterControl", "data", "temp"),
"Unexpected ApplicationTempDataPath Value");
return Task.CompletedTask;
}
}
}

View file

@ -0,0 +1,636 @@
/*
Copyright (c) 2022, Lars Brubaker
All rights reserved.
*/
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using MatterHackers.Agg;
using MatterHackers.Agg.Platform;
using MatterHackers.Agg.UI;
using MatterHackers.MatterControl.Tests.Automation;
using MatterHackers.VectorMath;
using NUnit.Framework;
using TestInvoker;
namespace MatterControl.Tests.MatterControl
{
// NOTE: These tests hang on GLFW currently as the window isn't closed properly.
[TestFixture, Category("PopupAnchorTests"), Parallelizable(ParallelScope.Children)]
public class PopupAnchorTests
{
[SetUp]
public void TestSetup()
{
StaticData.RootPath = MatterControlUtilities.StaticDataPath;
MatterControlUtilities.OverrideAppDataLocation(MatterControlUtilities.RootPath);
}
[Test, ChildProcessTest]
public async Task WindowTest()
{
var systemWindow = new PopupsTestWindow(700, 300)
{
Details = new Dictionary<string, string>()
{
["Task"] = "General Popup",
["Expected"] = "Popup should appear on click"
}
};
await systemWindow.RunTest(testRunner =>
{
systemWindow.Padding = systemWindow.Padding.Clone(bottom: 180);
var button = new ThemedTextButton("Popup", systemWindow.Theme)
{
Name = "targetA",
VAnchor = VAnchor.Bottom,
HAnchor = HAnchor.Left,
};
systemWindow.AddChild(button);
var color = Color.LightBlue;
button.Click += (s, e) =>
{
systemWindow.ShowPopup(
new ThemeConfig(),
new MatePoint()
{
Widget = button
},
new MatePoint()
{
Widget = new GuiWidget(180d, 100d)
{
BackgroundColor = color,
Border = 2,
BorderColor = color.Blend(Color.Black, 0.4)
}
});
};
testRunner.ClickByName("targetA");
testRunner.Delay();
return Task.CompletedTask;
}, 30);
}
[Test, ChildProcessTest]
public async Task TopBottomPopupTest()
{
var systemWindow = new PopupsTestWindow(800, 600)
{
Details = new Dictionary<string, string>()
{
["Task"] = "Top-Bottom Tests",
["Expected"] = "Popup bottoms should align with button top"
}
};
await AnchorTests(
systemWindow,
new MatePoint()
{
Mate = new MateOptions(MateEdge.Left, MateEdge.Top)
},
new MatePoint()
{
Mate = new MateOptions(MateEdge.Left, MateEdge.Bottom)
},
new ThemedTextButton("Popup", systemWindow.Theme)
{
Name = "buttonA",
VAnchor = VAnchor.Bottom,
},
(buttonWidget, popupWidget) =>
{
double buttonPosition = buttonWidget.TransformToScreenSpace(buttonWidget.LocalBounds).Top;
double popupPosition = popupWidget.TransformToScreenSpace(popupWidget.LocalBounds).Bottom;
Assert.AreEqual(buttonPosition, popupPosition);
});
}
[Test, ChildProcessTest]
public async Task TopTopPopupTest()
{
var systemWindow = new PopupsTestWindow(800, 600)
{
Details = new Dictionary<string, string>()
{
["Task"] = "Top-Bottom Tests",
["Expected"] = "Popup tops should align with button top"
}
};
await AnchorTests(
systemWindow,
new MatePoint()
{
Mate = new MateOptions(MateEdge.Left, MateEdge.Top)
},
new MatePoint()
{
Mate = new MateOptions(MateEdge.Left, MateEdge.Top)
},
new ThemedTextButton("Popup", systemWindow.Theme)
{
Name = "buttonA",
VAnchor = VAnchor.Bottom,
},
(buttonWidget, popupWidget) =>
{
double buttonPosition = buttonWidget.TransformToScreenSpace(buttonWidget.LocalBounds).Top;
double popupPosition = popupWidget.TransformToScreenSpace(popupWidget.LocalBounds).Top;
Assert.AreEqual(buttonPosition, popupPosition);
});
}
[Test, ChildProcessTest]
public async Task BottomTopPopupTest()
{
var systemWindow = new PopupsTestWindow(800, 600)
{
Details = new Dictionary<string, string>()
{
["Task"] = "Top-Bottom Tests",
["Expected"] = "Popup tops should align with button bottom"
}
};
await AnchorTests(
systemWindow,
new MatePoint()
{
Mate = new MateOptions(MateEdge.Right, MateEdge.Bottom)
},
new MatePoint()
{
Mate = new MateOptions(MateEdge.Left, MateEdge.Top)
},
new ThemedTextButton("Popup", systemWindow.Theme)
{
Name = "buttonA",
VAnchor = VAnchor.Bottom,
},
(buttonWidget, popupWidget) =>
{
double buttonPosition = buttonWidget.TransformToScreenSpace(buttonWidget.LocalBounds).Bottom;
double popupPosition = popupWidget.TransformToScreenSpace(popupWidget.LocalBounds).Top;
Assert.AreEqual(buttonPosition, popupPosition);
});
}
[Test, ChildProcessTest]
public async Task BottomBottomPopupTest()
{
var systemWindow = new PopupsTestWindow(800, 600)
{
Details = new Dictionary<string, string>()
{
["Task"] = "Bottom-Bottom Tests",
["Expected"] = "Popup bottoms should align with button bottom"
}
};
await AnchorTests(
systemWindow,
new MatePoint()
{
Mate = new MateOptions(MateEdge.Left, MateEdge.Bottom)
},
new MatePoint()
{
Mate = new MateOptions(MateEdge.Left, MateEdge.Bottom)
},
new ThemedTextButton("Popup", systemWindow.Theme)
{
Name = "buttonA",
VAnchor = VAnchor.Bottom,
},
(buttonWidget, popupWidget) =>
{
double buttonPosition = buttonWidget.TransformToScreenSpace(buttonWidget.LocalBounds).Bottom;
double popupPosition = popupWidget.TransformToScreenSpace(popupWidget.LocalBounds).Bottom;
Assert.AreEqual(buttonPosition, popupPosition);
});
}
// Redirect down to up
[Test, ChildProcessTest]
public async Task BottomTopUpRedirectTest()
{
var systemWindow = new PopupsTestWindow(800, 600)
{
Details = new Dictionary<string, string>()
{
["Task"] = "Top-Bottom Tests",
["Expected"] = "Popup tops should align with button bottom"
}
};
await AnchorTests(
systemWindow,
new MatePoint()
{
Mate = new MateOptions(MateEdge.Left, MateEdge.Bottom),
AltMate = new MateOptions(MateEdge.Left, MateEdge.Top)
},
new MatePoint()
{
Mate = new MateOptions(MateEdge.Left, MateEdge.Top),
AltMate = new MateOptions(MateEdge.Left, MateEdge.Bottom)
},
new ThemedTextButton("Popup", systemWindow.Theme)
{
Name = "buttonA",
VAnchor = VAnchor.Bottom,
},
(buttonWidget, popupWidget) =>
{
double buttonPosition = buttonWidget.TransformToScreenSpace(buttonWidget.LocalBounds).Top;
double popupPosition = popupWidget.TransformToScreenSpace(popupWidget.LocalBounds).Bottom;
Assert.AreEqual(buttonPosition, popupPosition);
},
(row) =>
{
row.VAnchor = VAnchor.Bottom | VAnchor.Fit;
});
}
[Test, ChildProcessTest]
public async Task TopTopUpRedirectTest()
{
var systemWindow = new PopupsTestWindow(800, 600)
{
Details = new Dictionary<string, string>()
{
["Task"] = "Top-Bottom Tests",
["Expected"] = "Popup tops should align with button top"
}
};
await AnchorTests(
systemWindow,
new MatePoint()
{
Mate = new MateOptions(MateEdge.Left, MateEdge.Top),
AltMate = new MateOptions(MateEdge.Left, MateEdge.Bottom)
},
new MatePoint()
{
Mate = new MateOptions(MateEdge.Left, MateEdge.Top),
AltMate = new MateOptions(MateEdge.Left, MateEdge.Bottom)
},
new ThemedTextButton("Popup", systemWindow.Theme)
{
Name = "buttonA",
VAnchor = VAnchor.Bottom,
},
(buttonWidget, popupWidget) =>
{
double buttonPosition = buttonWidget.TransformToScreenSpace(buttonWidget.LocalBounds).Bottom;
double popupPosition = popupWidget.TransformToScreenSpace(popupWidget.LocalBounds).Bottom;
Assert.AreEqual(buttonPosition, popupPosition);
},
(row) =>
{
row.VAnchor = VAnchor.Bottom | VAnchor.Fit;
});
}
// Redirect up to down
[Test, ChildProcessTest]
public async Task BottomTopDownRedirectTest()
{
var systemWindow = new PopupsTestWindow(800, 600)
{
Details = new Dictionary<string, string>()
{
["Task"] = "Top-Bottom Tests",
["Expected"] = "Popup bottoms should align with button top"
}
};
await AnchorTests(
systemWindow,
new MatePoint()
{
Mate = new MateOptions(MateEdge.Left, MateEdge.Top),
AltMate = new MateOptions(MateEdge.Left, MateEdge.Bottom),
},
new MatePoint()
{
Mate = new MateOptions(MateEdge.Left, MateEdge.Bottom),
AltMate = new MateOptions(MateEdge.Left, MateEdge.Top),
},
new ThemedTextButton("Popup", systemWindow.Theme)
{
Name = "buttonA",
VAnchor = VAnchor.Bottom,
},
(buttonWidget, popupWidget) =>
{
double buttonPosition = buttonWidget.TransformToScreenSpace(buttonWidget.LocalBounds).Bottom;
double popupPosition = popupWidget.TransformToScreenSpace(popupWidget.LocalBounds).Top;
Assert.AreEqual(buttonPosition, popupPosition);
},
(row) =>
{
row.VAnchor = VAnchor.Top | VAnchor.Fit;
});
}
[Test, ChildProcessTest]
public async Task TopTopDownRedirectTest()
{
var systemWindow = new PopupsTestWindow(800, 600)
{
Details = new Dictionary<string, string>()
{
["Task"] = "Bottom-Bottom Tests",
["Expected"] = "Popup bottoms should align with button bottom"
}
};
await AnchorTests(
systemWindow,
new MatePoint()
{
Mate = new MateOptions(MateEdge.Left, MateEdge.Bottom),
AltMate = new MateOptions(MateEdge.Right, MateEdge.Top),
},
new MatePoint()
{
Mate = new MateOptions(MateEdge.Left, MateEdge.Bottom),
AltMate = new MateOptions(MateEdge.Left, MateEdge.Top),
},
new ThemedTextButton("Popup", systemWindow.Theme)
{
Name = "buttonA",
VAnchor = VAnchor.Bottom,
},
(buttonWidget, popupWidget) =>
{
double buttonPosition = buttonWidget.TransformToScreenSpace(buttonWidget.LocalBounds).Top;
double popupPosition = popupWidget.TransformToScreenSpace(popupWidget.LocalBounds).Top;
Assert.AreEqual(buttonPosition, popupPosition);
},
(row) =>
{
row.VAnchor = VAnchor.Top | VAnchor.Fit;
});
}
// Redirect left to right
[Test, ChildProcessTest]
public async Task LeftRightRedirectTest()
{
var systemWindow = new PopupsTestWindow(800, 600)
{
Details = new Dictionary<string, string>()
{
["Task"] = "Top-Bottom Tests",
["Expected"] = "Popup tops should align with button bottom"
}
};
systemWindow.Padding = systemWindow.Padding.Clone(left: 0);
int i = 0;
await AnchorTests(
systemWindow,
new MatePoint()
{
Mate = new MateOptions(MateEdge.Left, MateEdge.Bottom),
AltMate = new MateOptions(MateEdge.Left, MateEdge.Top)
},
new MatePoint()
{
Mate = new MateOptions(MateEdge.Left, MateEdge.Top),
AltMate = new MateOptions(MateEdge.Left, MateEdge.Bottom)
},
new ThemedTextButton("Popup", systemWindow.Theme)
{
Name = "buttonA",
VAnchor = VAnchor.Bottom,
},
(buttonWidget, popupWidget) =>
{
double buttonPosition = buttonWidget.TransformToScreenSpace(buttonWidget.LocalBounds).Left;
double popupPosition = popupWidget.TransformToScreenSpace(popupWidget.LocalBounds).Left;
if (i++ > 2)
{
// Switch to anchor right aligned for the last case
buttonPosition = buttonWidget.TransformToScreenSpace(buttonWidget.LocalBounds).Right;
}
Assert.AreEqual(buttonPosition, popupPosition);
},
(row) =>
{
// Clear left margin so menus clip
row.Margin = 2;
});
}
// Redirect right to left
[Test, ChildProcessTest]
public async Task RightLeftRedirectTest()
{
var systemWindow = new PopupsTestWindow(800, 600)
{
Details = new Dictionary<string, string>()
{
["Task"] = "Bottom-Bottom Tests",
["Expected"] = "Popup bottoms should align with button bottom"
}
};
int i = 0;
await AnchorTests(
systemWindow,
new MatePoint()
{
Mate = new MateOptions(MateEdge.Left, MateEdge.Bottom),
AltMate = new MateOptions(MateEdge.Left, MateEdge.Top),
},
new MatePoint()
{
Mate = new MateOptions(MateEdge.Left, MateEdge.Bottom),
AltMate = new MateOptions(MateEdge.Right, MateEdge.Top),
},
new ThemedTextButton("Popup", systemWindow.Theme)
{
Name = "buttonA",
VAnchor = VAnchor.Bottom,
},
(buttonWidget, popupWidget) =>
{
double buttonPosition = buttonWidget.TransformToScreenSpace(buttonWidget.LocalBounds).Left;
double popupPosition = popupWidget.TransformToScreenSpace(popupWidget.LocalBounds).Right;
if (i++ == 2)
{
// Switch to anchor right aligned for the last case
buttonPosition = buttonWidget.TransformToScreenSpace(buttonWidget.LocalBounds).Right;
}
Assert.AreEqual(buttonPosition, popupPosition);
},
(row) =>
{
row.HAnchor = HAnchor.Right | HAnchor.Fit;
row.VAnchor = VAnchor.Center | VAnchor.Fit;
});
}
private static async Task AnchorTests(PopupsTestWindow systemWindow, MatePoint anchor, MatePoint popup, ThemedTextButton button, Action<GuiWidget, GuiWidget> validator, Action<GuiWidget> rowAdjuster = null)
{
await systemWindow.RunTest(testRunner =>
{
button.BackgroundColor = Color.LightGray;
button.HoverColor = Color.LightBlue;
button.MouseDownColor = Color.Magenta;
var row = new FlowLayoutWidget()
{
VAnchor = VAnchor.Center | VAnchor.Fit,
HAnchor = HAnchor.Left | HAnchor.Fit,
Margin = new BorderDouble(left: 120)
};
systemWindow.AddChild(row);
row.AddChild(button);
rowAdjuster?.Invoke(row);
button.Click += (s, e) =>
{
popup.Widget = new GuiWidget(180d, 100d)
{
BackgroundColor = Color.LightBlue,
Border = 2,
BorderColor = Color.LightBlue.Blend(Color.Black, 0.4)
};
systemWindow.ShowPopup(new ThemeConfig(), anchor, popup);
};
anchor.Widget = button;
for (var i = 0; i < 4; i++)
{
switch (i)
{
case 0:
anchor.Mate.HorizontalEdge = MateEdge.Left;
popup.Mate.HorizontalEdge = MateEdge.Right;
break;
case 1:
anchor.Mate.HorizontalEdge = MateEdge.Left;
popup.Mate.HorizontalEdge = MateEdge.Left;
break;
case 2:
anchor.Mate.HorizontalEdge = MateEdge.Right;
popup.Mate.HorizontalEdge = MateEdge.Right;
break;
case 3:
anchor.Mate.HorizontalEdge = MateEdge.Right;
popup.Mate.HorizontalEdge = MateEdge.Left;
break;
}
testRunner.ClickByName("buttonA");
testRunner.Delay();
validator.Invoke(button, popup.Widget);
popup.Widget.Unfocus();
}
testRunner.Delay();
return Task.CompletedTask;
}, 25);
}
public class PopupsTestWindow : SystemWindow
{
private FlowLayoutWidget column;
public ThemeConfig Theme { get; }
public PopupsTestWindow(int width, int height)
: base(width, height)
{
this.BackgroundColor = new Color(56, 56, 56);
Theme = new ThemeConfig();
this.Padding = new BorderDouble(left: 120, bottom: 10, right: 10, top: 10);
column = new FlowLayoutWidget(FlowDirection.TopToBottom)
{
HAnchor = HAnchor.Right | HAnchor.Fit,
VAnchor = VAnchor.Stretch,
};
this.AddChild(column);
}
private Dictionary<string, string> _details;
public Dictionary<string, string> Details
{
get => _details;
set
{
_details = value;
foreach (var kvp in value)
{
this.ShowDetails(kvp.Key, kvp.Value);
}
}
}
public void ShowDetails(string heading, string text)
{
// Store
var row = new FlowLayoutWidget
{
VAnchor = VAnchor.Fit,
HAnchor = HAnchor.Left | HAnchor.Fit
};
column.AddChild(row);
row.AddChild(new TextWidget(heading + ":", textColor: Color.White, pointSize: 9)
{
MinimumSize = new Vector2(80, 0)
});
row.AddChild(new TextWidget(text, textColor: Color.White, pointSize: 9));
}
}
}
}

View file

@ -0,0 +1,321 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using MatterHackers.Agg;
using MatterHackers.Agg.Platform;
using MatterHackers.MatterControl.SlicerConfiguration;
using MatterHackers.MatterControl.Tests.Automation;
using NUnit.Framework;
namespace MatterControl.Tests.MatterControl
{
[TestFixture]
public class PrinterConfigurationTests
{
[Test, Category("PrinterConfigurationFiles"), Ignore("Not Finished/previously ignored")]
public void PrinterConfigTests()
{
string staticDataPath = MatterControlUtilities.StaticDataPath;
StaticData.RootPath = staticDataPath;
var profilesDirectory = new DirectoryInfo(Path.Combine(staticDataPath, "Profiles"));
foreach (FileInfo file in profilesDirectory.GetFiles("*.printer", SearchOption.AllDirectories))
{
var printerSettings = PrinterSettings.LoadFile(file.FullName);
// Assert that no UserLayer values exist in production .printer files
Assert.AreEqual(0, printerSettings.UserLayer.Keys.Count, ".printer files should not contain UserLayer values");
var layersToInspect = new List<PrinterSettingsLayer>();
layersToInspect.Add(printerSettings.OemLayer);
layersToInspect.AddRange(printerSettings.MaterialLayers);
layersToInspect.AddRange(printerSettings.QualityLayers);
// Validate each PrinterSettingLayer in the .printer file
foreach (var layer in layersToInspect.Where(l => l.Keys.Any()))
{
firstLayerSpeedEqualsAcceptableValue(printerSettings, layer, file.FullName);
firstLayerHeightLessThanNozzleDiameter(printerSettings, layer, file.FullName);
layerHeightLessThanNozzleDiameter(printerSettings, layer, file.FullName);
firstLayerExtrusionWidthAcceptableValue(printerSettings, layer, file.FullName);
firstLayerExtrusionWidthNotZero(layer, file.FullName);
bedSizeXYSeparatedByComma(layer, file.FullName);
printCenterFormatSeparatedByComma(layer, file.FullName);
testRetractLengthLessThanTwenty(layer, file.FullName);
testExtruderCountGreaterThanZero(layer, file.FullName);
minimumFanSpeedLessThanOrEqualToOneHundred(layer, file.FullName);
maxFanSpeedNotGreaterThanOneHundred(layer, file.FullName);
noCurlyBracketsInStartGcode(layer, file.FullName);
noCurlyBracketsInEndGcode(layer, file.FullName);
testBottomSolidLayersOneMM(layer, file.FullName);
testFirstLayerTempNotInStartGcode(layer, file.FullName);
testFirstLayerBedTemperatureNotInStartGcode(layer, file.FullName);
}
}
}
public void firstLayerSpeedEqualsAcceptableValue(PrinterSettings settings, PrinterSettingsLayer layer, string sourceFile)
{
string firstLayerSpeedString;
if (!layer.TryGetValue(SettingsKey.first_layer_speed, out firstLayerSpeedString))
{
return;
}
double firstLayerSpeed;
if (firstLayerSpeedString.Contains("%"))
{
string infillSpeedString = settings.GetValue(SettingsKey.infill_speed);
double infillSpeed = double.Parse(infillSpeedString);
firstLayerSpeedString = firstLayerSpeedString.Replace("%", "");
double FirstLayerSpeedPercent = double.Parse(firstLayerSpeedString);
firstLayerSpeed = FirstLayerSpeedPercent * infillSpeed / 100.0;
}
else
{
firstLayerSpeed = double.Parse(firstLayerSpeedString);
}
Assert.Greater(firstLayerSpeed, 5, "Unexpected firstLayerSpeedEqualsAcceptableValue value: " + sourceFile);
}
public void firstLayerHeightLessThanNozzleDiameter(PrinterSettings printerSettings, PrinterSettingsLayer layer, string sourceFile)
{
string firstLayerHeight;
if (!layer.TryGetValue(SettingsKey.first_layer_height, out firstLayerHeight))
{
return;
}
float convertedFirstLayerHeightValue;
if (firstLayerHeight.Contains("%"))
{
string reFormatLayerHeight = firstLayerHeight.Replace("%", " ");
convertedFirstLayerHeightValue = float.Parse(reFormatLayerHeight) / 100;
}
else
{
convertedFirstLayerHeightValue = float.Parse(firstLayerHeight);
}
string nozzleDiameter = printerSettings.GetValue(SettingsKey.nozzle_diameter);
Assert.LessOrEqual(convertedFirstLayerHeightValue, float.Parse(nozzleDiameter), "Unexpected firstLayerHeightLessThanNozzleDiameter value: " + sourceFile);
}
public void firstLayerExtrusionWidthAcceptableValue(PrinterSettings printerSettings, PrinterSettingsLayer layer, string sourceFile)
{
string firstLayerExtrusionWidth;
if (!layer.TryGetValue(SettingsKey.first_layer_extrusion_width, out firstLayerExtrusionWidth))
{
return;
}
float convertedFirstLayerExtrusionWidth;
string nozzleDiameter = printerSettings.GetValue(SettingsKey.nozzle_diameter);
float acceptableValue = float.Parse(nozzleDiameter) * 4;
if (firstLayerExtrusionWidth.Contains("%"))
{
string reformatFirstLayerExtrusionWidth = firstLayerExtrusionWidth.Replace("%", " ");
convertedFirstLayerExtrusionWidth = float.Parse(reformatFirstLayerExtrusionWidth) / 100;
}
else
{
convertedFirstLayerExtrusionWidth = float.Parse(firstLayerExtrusionWidth);
}
Assert.LessOrEqual(convertedFirstLayerExtrusionWidth, acceptableValue, "Unexpected firstLayerExtrusionWidthAcceptableValue value: " + sourceFile);
}
public void firstLayerExtrusionWidthNotZero(PrinterSettingsLayer layer, string sourceFile)
{
string firstLayerExtrusionWidth;
if (!layer.TryGetValue(SettingsKey.first_layer_extrusion_width, out firstLayerExtrusionWidth))
{
return;
}
float convertedFirstLayerExtrusionWidth;
if(firstLayerExtrusionWidth.Contains("%"))
{
string reformatFirstLayerExtrusionWidth = firstLayerExtrusionWidth.Replace("%", " ");
convertedFirstLayerExtrusionWidth = float.Parse(reformatFirstLayerExtrusionWidth);
}
else
{
convertedFirstLayerExtrusionWidth = float.Parse(firstLayerExtrusionWidth);
}
Assert.AreNotEqual(0, convertedFirstLayerExtrusionWidth, "Unexpected firstLayerExtrusionWidthNotZero value: " + sourceFile);
}
public void layerHeightLessThanNozzleDiameter(PrinterSettings printerSettings, PrinterSettingsLayer layer, string sourceFile)
{
string layerHeight;
if (!layer.TryGetValue(SettingsKey.layer_height, out layerHeight))
{
return;
}
float convertedLayerHeight = float.Parse(layerHeight);
string nozzleDiameter = printerSettings.GetValue(SettingsKey.nozzle_diameter);
float convertedNozzleDiameterValue = float.Parse(nozzleDiameter);
Assert.LessOrEqual(convertedLayerHeight, convertedNozzleDiameterValue, "Unexpected layerHeightLessThanNozzleDiameter value: " + sourceFile);
}
public void bedSizeXYSeparatedByComma(PrinterSettingsLayer layer, string sourceFile)
{
string settingValue;
if(!layer.TryGetValue(SettingsKey.bed_size, out settingValue))
{
return;
}
string[] settingValueToTest = settingValue.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
Assert.AreEqual(2, settingValueToTest.Length, "bed_size should have two values separated by a comma: " + sourceFile);
}
public void printCenterFormatSeparatedByComma(PrinterSettingsLayer layer, string sourceFile)
{
string settingValue;
if (!layer.TryGetValue(SettingsKey.print_center, out settingValue))
{
return;
}
string[] settingValueToTest = settingValue.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
Assert.AreEqual(2, settingValueToTest.Length, "print_center should have two values separated by a comma: " + sourceFile);
}
public void testRetractLengthLessThanTwenty(PrinterSettingsLayer layer, string sourceFile)
{
string settingValue;
if (!layer.TryGetValue(SettingsKey.retract_length, out settingValue))
{
return;
}
Assert.Less(float.Parse(settingValue, CultureInfo.InvariantCulture.NumberFormat), 20, "retract_length should be less than 20: " + sourceFile);
}
public void testExtruderCountGreaterThanZero(PrinterSettingsLayer layer, string sourceFile)
{
string settingValue;
if (!layer.TryGetValue("extruder_count", out settingValue))
{
return;
}
Assert.Greater(int.Parse(settingValue), 0, "extruder_count should be greater than zero: " + sourceFile);
}
public void minimumFanSpeedLessThanOrEqualToOneHundred(PrinterSettingsLayer layer, string sourceFile)
{
string settingValue;
if (!layer.TryGetValue(SettingsKey.min_fan_speed, out settingValue))
{
return;
}
Assert.LessOrEqual(int.Parse(settingValue), 100, "min_fan_speed should be less than or equal to 100: " + sourceFile);
}
public void maxFanSpeedNotGreaterThanOneHundred(PrinterSettingsLayer layer, string sourceFile)
{
string settingValue;
if (!layer.TryGetValue(SettingsKey.max_fan_speed, out settingValue))
{
return;
}
Assert.LessOrEqual(int.Parse(settingValue), 100, "max_fan_speed should be less than or equal to 100: " + sourceFile);
}
public void noCurlyBracketsInStartGcode(PrinterSettingsLayer layer, string sourceFile)
{
string settingValue;
if (!layer.TryGetValue(SettingsKey.start_gcode, out settingValue))
{
return;
}
Assert.IsFalse(settingValue.Contains("{"), "start_gcode should not contain braces: " + sourceFile);
}
public void noCurlyBracketsInEndGcode(PrinterSettingsLayer layer, string sourceFile)
{
string settingValue;
if (!layer.TryGetValue(SettingsKey.end_gcode, out settingValue))
{
return;
}
Assert.False(settingValue.Contains("{"), "end_gcode should not contain braces: " + sourceFile);
}
public void testBottomSolidLayersOneMM(PrinterSettingsLayer layer, string sourceFile)
{
string settingValue;
if (!layer.TryGetValue(SettingsKey.bottom_solid_layers, out settingValue))
{
return;
}
Assert.AreEqual("1mm", settingValue, "bottom_solid_layers should be 1mm: " + sourceFile);
}
public void testFirstLayerTempNotInStartGcode(PrinterSettingsLayer layer, string sourceFile)
{
string settingValue;
if (!layer.TryGetValue(SettingsKey.start_gcode, out settingValue))
{
return;
}
Assert.False(settingValue.Contains(SettingsKey.first_layer_temperature), "start_gcode should not contain first_layer_temperature: " + sourceFile);
}
public void testFirstLayerBedTemperatureNotInStartGcode(PrinterSettingsLayer layer, string sourceFile)
{
string settingValue;
if (!layer.TryGetValue(SettingsKey.start_gcode, out settingValue))
{
return;
}
Assert.False(settingValue.Contains(SettingsKey.first_layer_bed_temperature), "start_gcode should not contain first_layer_bed_temperature: " + sourceFile);
}
}
}

View file

@ -0,0 +1,102 @@
using System;
using System.Collections.Generic;
using MatterHackers.Agg;
using MatterHackers.Agg.Platform;
using MatterHackers.MatterControl;
using MatterHackers.MatterControl.ConfigurationPage.PrintLeveling;
using MatterHackers.MatterControl.SlicerConfiguration;
using MatterHackers.MatterControl.Tests.Automation;
using Newtonsoft.Json.Linq;
using NUnit.Framework;
namespace MatterControl.Tests.MatterControl
{
[TestFixture, Category("PrinterSettings")]
public class PrinterSettingsTests
{
[Test]
public void StartGCodeHasHeating()
{
StaticData.RootPath = MatterControlUtilities.StaticDataPath;
MatterControlUtilities.OverrideAppDataLocation(MatterControlUtilities.RootPath);
var printer = new PrinterConfig(new PrinterSettings());
printer.Settings.Slicer = new EngineMappingsMatterSlice();
Slicer.ExtrudersUsed = new List<bool> { true };
var extruderTemp = printer.Settings.GetValue<double>(SettingsKey.temperature);
Assert.IsTrue(extruderTemp > 0);
var bedTemp = printer.Settings.GetValue<double>(SettingsKey.bed_temperature);
Assert.IsTrue(bedTemp > 0);
string result = printer.Settings.ResolveValue(SettingsKey.start_gcode);
// Pass start_gcode through exportField converter
var exportField = printer.Settings.Slicer.Exports[SettingsKey.start_gcode];
result = exportField.Converter(result, printer.Settings);
var beforeAndAfter = result.Split(new string[] { "; settings from start_gcode" }, StringSplitOptions.None);
Assert.AreEqual(2, beforeAndAfter.Length);
Assert.IsTrue(beforeAndAfter[0].Contains($"M104 T0 S{extruderTemp}"));
Assert.IsTrue(beforeAndAfter[0].Contains($"M140 S{bedTemp}"));
Assert.IsFalse(beforeAndAfter[0].Contains($"M109 T0 S{extruderTemp}"));
Assert.IsFalse(beforeAndAfter[0].Contains($"M190 S{bedTemp}"));
Assert.IsTrue(beforeAndAfter[1].Contains($"M109 T0 S{extruderTemp}"));
Assert.IsTrue(beforeAndAfter[1].Contains($"M190 S{bedTemp}"));
// set mapping when there is an M109 in the start code
printer.Settings.SetValue(SettingsKey.start_gcode, "G28\\nM109 S205");
string result2 = printer.Settings.ResolveValue(SettingsKey.start_gcode);
// Pass start_gcode through exportField converter
result2 = exportField.Converter(result2, printer.Settings);
beforeAndAfter = result2.Split(new string[] { "; settings from start_gcode" }, StringSplitOptions.None);
// the main change is there should be an M190 before and not after the start code
Assert.AreEqual(2, beforeAndAfter.Length);
Assert.IsTrue(beforeAndAfter[0].Contains($"M104 T0 S{extruderTemp}"));
Assert.IsTrue(beforeAndAfter[0].Contains($"M140 S{bedTemp}"));
Assert.IsFalse(beforeAndAfter[0].Contains($"M109 T0 S{extruderTemp}"));
Assert.IsTrue(beforeAndAfter[0].Contains($"M190 S{bedTemp}"));
Assert.IsFalse(beforeAndAfter[1].Contains($"M109 T0 S{extruderTemp}"), "M109 already in gcode, should not be in after.");
Assert.IsFalse(beforeAndAfter[1].Contains($"M190 S{bedTemp}"));
}
[Test]
public void ExpectedPropertiesOnlyTest()
{
StaticData.RootPath = MatterControlUtilities.StaticDataPath;
MatterControlUtilities.OverrideAppDataLocation(MatterControlUtilities.RootPath);
var expectedProperties = new HashSet<string>(
new[]
{
"DocumentVersion",
"ID",
"StagedUserSettings",
"Macros",
"OemLayer",
"UserLayer",
"MaterialLayers",
"QualityLayers"
});
var printer = new PrinterConfig(new PrinterSettings());
var levelingSolution = new LevelWizard3Point(printer);
var printerSettings = printer.Settings;
var json = printer.Settings.ToJson();
var jObject = JObject.Parse(json);
foreach (var item in jObject)
{
Assert.IsTrue(expectedProperties.Contains(item.Key), $"Unexpected property ({item.Key}) in PrinterSettings - add to list or use @JsonIgnore");
}
}
}
}

View file

@ -0,0 +1,36 @@
using System.IO;
using System.Linq;
using MatterHackers.MatterControl.Tests.Automation;
using NUnit.Framework;
namespace MatterControl.Tests.MatterControl
{
[TestFixture]
class PrinterWhiteListTests
{
[Test, Category("PrinterWhiteListTests")]
public void DesktopCalibrationPartsInSettings()
{
string settingsJsonPath = Path.Combine(MatterControlUtilities.StaticDataPath, "OEMSettings", "Settings.json");
if (File.Exists(settingsJsonPath))
{
string[] lines = File.ReadAllLines(settingsJsonPath);
bool hasCoin = lines.Where(l => l.Contains("\"MatterControl - Coin.stl\",")).Any();
bool hasTabletStand = lines.Where(l => l.Contains("\"MatterControl - Stand.stl\",")).Any();
Assert.IsTrue(hasCoin, "Expected coin file not found");
Assert.IsTrue(hasTabletStand, "Expected stand file not found");
}
}
[Test, Category("SamplePartsTests")]
public void DesktopCalibrationPartsExist()
{
string samplePartsPath = Path.Combine(MatterControlUtilities.StaticDataPath, "OEMSettings", "SampleParts");
string[] files = Directory.GetFiles(samplePartsPath);
bool hasPhil = files.Where(l => l.Contains("Phil A Ment.stl")).Any();
Assert.IsTrue(hasPhil, "Expected Phil file not found");
}
}
}

View file

@ -0,0 +1,149 @@
#if !__ANDROID__
using MatterHackers.MatterControl.Tests.Automation;
#endif
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Xml.Linq;
using System.Threading.Tasks;
using System.Threading;
using MatterHackers.MatterControl;
using TestInvoker;
namespace MatterControl.Tests
{
[TestFixture, Parallelizable(ParallelScope.Children)]
public class ReleaseBuildTests
{
private static Type debuggableAttribute = typeof(DebuggableAttribute);
[Test, ChildProcessTest, Category("ReleaseQuality")]
public void MatterControlAssemblyIsOptimized()
{
#if (!DEBUG)
IsAssemblyOptimized(Assembly.Load("MatterControlLib, Culture=neutral, PublicKeyToken=null"));
#endif
}
[Test, ChildProcessTest, Category("ReleaseQuality")]
public void MatterControlKnownAssembliesAreOptimized()
{
//MatterHackers.RenderOpenGl.dll
// This list can be refreshed via the rebuildDependencies() helper function below
string knownAssemblies = @"VectorMath.dll
Agg.dll
PolygonMesh.dll
agg_clipper_library.dll
Gui.dll
Tesselate.dll
DataConverters2D.dll
DataConverters3D.dll
Localizations.dll
Community.CsharpSqlite.dll
MatterHackers.Agg.ImageProcessing.dll
MatterHackers.MarchingSquares.dll
GuiAutomation.dll";
foreach (string assemblyName in knownAssemblies.Split('\n').Select(s => s.Trim()))
{
var assemblyPath = Path.Combine(MatterControlUtilities.MainBinOutputPath, assemblyName);
// Missing/renamed assemblies should fail the test and force a correction
Assert.IsTrue(File.Exists(assemblyPath), "Assembly missing: " + assemblyPath);
#if (!DEBUG)
var assembly = Assembly.LoadFrom(assemblyPath);
IsAssemblyOptimized(assembly);
#endif
}
}
private void rebuildDependencies()
{
// Modify path to point at a recent BuildAgent results file
var elem = XElement.Load(@"C:\Data\Sources\MatterHackers\BuildAndDeployment\MatterControl\build_sln.xml");
var items = elem.Descendants().Where(e => e.Name == "target" && "CopyFilesToOutputDirectory" == (string)e.Attribute("name")).SelectMany(e => e.Elements("message").Select(e2 => e2.Value.TrimEnd('.')).Where(s => s.Contains("Copying") && s.Contains(".dll")));
var referencedItems = new List<string>();
foreach (var item in items)
{
var segments = System.Text.RegularExpressions.Regex.Split(item, "to \"");
var relativeAssemblyName = segments[1].TrimEnd('"');
var assemblyName = Path.GetFileName(relativeAssemblyName);
referencedItems.Add(assemblyName);
}
Console.WriteLine(referencedItems);
}
#if !__ANDROID__
[Test, ChildProcessTest]
public async Task MatterControlRuns()
{
await MatterControlUtilities.RunTest((testRunner) =>
{
testRunner.WaitForName("PartPreviewContent");
Assert.IsTrue(testRunner.NameExists("PartPreviewContent"));
return Task.CompletedTask;
});
}
#endif
[Test, ChildProcessTest, Category("ReleaseQuality")]
public void MatterControlDependenciesAreOptimized()
{
#if (!DEBUG)
var matterControl = Assembly.Load("MatterControlLib, Culture=neutral, PublicKeyToken=null");
// Loop over all referenced assemblies to verify they are optimized and lack (symbols and Debug compile flag)
foreach (var assemblyName in matterControl.GetReferencedAssemblies())
{
var assembly = Assembly.Load(assemblyName.FullName);
var firstNamespace = assembly?.GetTypes()?.FirstOrDefault()?.Namespace;
// Only validate our assemblies
if (firstNamespace != null && (firstNamespace.Contains("MatterHackers") || firstNamespace.Contains("MatterControl")))
{
IsAssemblyOptimized(assembly);
}
}
#endif
}
[Test, ChildProcessTest, Category("ReleaseQuality")]
public void ClassicDebugComplicationFlagTests()
{
#if (!DEBUG)
BuildValidationTests.CheckKnownAssemblyConditionalCompSymbols();
#endif
}
private static void IsAssemblyOptimized(Assembly assm)
{
var matchedAttributes = assm.GetCustomAttributes(debuggableAttribute, false);
var assemblyName = assm.GetName();
if (matchedAttributes.Count() == 0)
{
Assert.Inconclusive("Symbols likely missing from Release build: " + assemblyName.FullName + ". \r\n\r\nTo resolve the issue, switch Project Properties -> Build -> Advanced -> Debug Info property to 'pdb-only'");
}
var debuggable = matchedAttributes.First() as DebuggableAttribute;
Assert.IsFalse(debuggable.IsJITOptimizerDisabled, "Referenced assembly is not optimized: " + assemblyName.Name);
Assert.IsFalse(debuggable.IsJITTrackingEnabled, "Referenced assembly has symbols: " + assemblyName.Name);
Console.WriteLine("Assembly is optimized: " + assemblyName.Name);
}
}
}

View file

@ -0,0 +1,144 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using MatterHackers.Agg;
using MatterHackers.Agg.Platform;
using MatterHackers.Agg.UI;
using MatterHackers.MatterControl;
using MatterHackers.MatterControl.ConfigurationPage.PrintLeveling;
using MatterHackers.MatterControl.SlicerConfiguration;
using MatterHackers.MatterControl.Tests.Automation;
using MatterHackers.VectorMath;
using Newtonsoft.Json;
using NUnit.Framework;
namespace MatterControl.Tests.MatterControl
{
[TestFixture, Category("ConfigIni")]
public class SettingsParseTests
{
[Test]
public void Check3PointLevelingPositions()
{
StaticData.RootPath = MatterControlUtilities.StaticDataPath;
MatterControlUtilities.OverrideAppDataLocation(MatterControlUtilities.RootPath);
var printer = new PrinterConfig(new PrinterSettings());
var levelingSolution = new LevelWizard3Point(printer);
var printerSettings = printer.Settings;
{
var samples = levelingSolution.GetPositionsToSample(default(Vector3)).ToList();
Assert.AreEqual("200,200", printerSettings.GetValue(SettingsKey.bed_size));
Assert.AreEqual("100,100", printerSettings.GetValue(SettingsKey.print_center));
Assert.AreEqual("rectangular", printerSettings.GetValue(SettingsKey.bed_shape));
Assert.AreEqual(new Vector2(20, 20), samples[0]);
Assert.AreEqual(new Vector2(180, 20), samples[1]);
Assert.AreEqual(new Vector2(100, 180), samples[2]);
}
}
[Test]
public void SupportInterfaceMaterialAssignedToExtruderOne()
{
StaticData.RootPath = MatterControlUtilities.StaticDataPath;
MatterControlUtilities.OverrideAppDataLocation(MatterControlUtilities.RootPath);
// first_layer_extrusion_width
{
// percent first layer extrusion width
{
string[] settings = new string[] { SettingsKey.first_layer_extrusion_width, "%150", SettingsKey.nozzle_diameter, ".4" };
Assert.AreEqual(GetProfile(settings).GetValue<double>(SettingsKey.first_layer_extrusion_width), .6, .0001);
}
// absolute first layer extrusion width
{
string[] settings = new string[] { SettingsKey.first_layer_extrusion_width, ".75", SettingsKey.nozzle_diameter, ".4" };
Assert.AreEqual(GetProfile(settings).GetValue<double>(SettingsKey.first_layer_extrusion_width), .75, .0001);
}
// 0 first layer extrusion width
{
string[] settings = new string[] { SettingsKey.first_layer_extrusion_width, "0", SettingsKey.nozzle_diameter, ".4" };
Assert.AreEqual(GetProfile(settings).GetValue<double>(SettingsKey.first_layer_extrusion_width), .4, .0001);
}
}
// extruder_count
{
// normal single
{
string[] settings = new string[] { SettingsKey.extruder_count, "1", SettingsKey.extruders_share_temperature, "0" };
Assert.AreEqual(GetProfile(settings).GetValue<int>(SettingsKey.extruder_count), 1);
}
// normal multiple
{
string[] settings = new string[] { SettingsKey.extruder_count, "2", SettingsKey.extruders_share_temperature, "0" };
Assert.AreEqual(GetProfile(settings).GetValue<int>(SettingsKey.extruder_count), 2);
}
// shared temp
{
string[] settings = new string[] { SettingsKey.extruder_count, "2", SettingsKey.extruders_share_temperature, "1" };
Assert.AreEqual(GetProfile(settings).Helpers.HotendCount(), 1);
}
}
}
[Test]
// Validates that all SetSettingsOnChange linked fields exist and have their required TargetSetting and Value definitions
public void LinkedSettingsExist()
{
StaticData.RootPath = MatterControlUtilities.StaticDataPath;
MatterControlUtilities.OverrideAppDataLocation(MatterControlUtilities.RootPath);
var settingsByName = PrinterSettings.SettingsData;
var allSettings = settingsByName.Values;
foreach (var boundSetting in allSettings.Where(s => s.SetSettingsOnChange.Count > 0))
{
foreach (var linkedSetting in boundSetting.SetSettingsOnChange)
{
// TargetSetting definition must exist
Assert.IsTrue(linkedSetting.TryGetValue("TargetSetting", out string targetSettingSource), "TargetSetting field should exist");
// TargetSetting source field must be defined/known
Assert.IsTrue(settingsByName.ContainsKey(targetSettingSource), "Linked field should exist: " + targetSettingSource);
}
}
}
[Test]
public void PresentationNamesLackColon()
{
StaticData.RootPath = MatterControlUtilities.StaticDataPath;
MatterControlUtilities.OverrideAppDataLocation(MatterControlUtilities.RootPath);
var allSettings = PrinterSettings.SettingsData.Values;
foreach (var setting in allSettings)
{
// TargetSetting source field must be defined/known
Assert.IsFalse(setting.PresentationName.Trim().EndsWith(":"), $"Presentation name should not end with trailing colon: '{setting.PresentationName}'");
}
}
PrinterSettings GetProfile(string[] settings)
{
Dictionary<string, string> dictionary = new Dictionary<string, string>();
for (int i = 0; i < settings.Length; i += 2)
{
dictionary.Add(settings[i], settings[i + 1]);
}
var profile = new PrinterSettings()
{
OemLayer = new PrinterSettingsLayer(dictionary)
};
return profile;
}
}
}

View file

@ -0,0 +1,741 @@
/*
Copyright (c) 2018, 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.Diagnostics;
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.Agg.UI.Tests;
using MatterHackers.GuiAutomation;
using MatterHackers.MatterControl;
using MatterHackers.MatterControl.SlicerConfiguration;
using MatterHackers.MatterControl.Tests.Automation;
using MatterHackers.SerialPortCommunication.FrostedSerial;
using NUnit.Framework;
using static MatterControl.Tests.MatterControl.SliceSettingsFieldTests;
using TestInvoker;
namespace MatterControl.Tests.MatterControl
{
public static class RunnerX
{
public static Task RunTest(this SystemWindow systemWindow, AutomationTest automationTest, int timeout)
{
return AutomationRunner.ShowWindowAndExecuteTests(systemWindow, automationTest, timeout);
}
[DebuggerStepThrough]
public static void Add(this List<ValueMap> valueMap, string input, string expected)
{
valueMap.Add(new ValueMap(input, expected));
}
}
// NOTE: These tests hang on GLFW currently as the window isn't closed properly.
[TestFixture, Category("SliceSettingsTests"), Parallelizable(ParallelScope.Children)]
public class SliceSettingsFieldTests
{
[SetUp]
public void TestSetup()
{
StaticData.RootPath = MatterControlUtilities.StaticDataPath;
MatterControlUtilities.OverrideAppDataLocation(MatterControlUtilities.RootPath);
}
[Test, ChildProcessTest]
public Task TestExistsForEachUIFieldType()
{
var testClass = this.GetType();
var thisClassMethods = testClass.GetMethods(BindingFlags.Public | BindingFlags.Instance);
// Find and validate all UIField types, skipping abstract classes
foreach (var fieldType in PluginFinder.FindTypes<UIField>().Where(fieldType => !fieldType.IsAbstract))
{
if (fieldType.Name == "UIField")
{
continue;
}
string expectedTestName = $"{fieldType.Name}Test";
Assert.AreEqual(
1,
thisClassMethods.Where(m => m.Name == expectedTestName).Count(),
"Required test missing: " + expectedTestName);
}
return Task.CompletedTask;
}
[Test, ChildProcessTest]
public async Task DoubleFieldTest()
{
var theme = MatterHackers.MatterControl.AppContext.Theme;
var testField = new DoubleField(theme);
await ValidateAgainstValueMap(
testField,
theme,
(field) => (field.Content as ThemedNumberEdit).ActuallNumberEdit.Text,
new List<ValueMap>()
{
{ "0.12345", "0.12345" },
{ "1.2345", "1.2345" },
{ "12.345", "12.345" },
{ "12.7", "12.7" },
{ "+0.12345", "0.12345" },
{ "+1.2345", "1.2345" },
{ "+12.345", "12.345" },
{ "-0.12345", "-0.12345" },
{ "-1.2345", "-1.2345" },
{ "-12.345", "-12.345" },
{ "12.7", "12.7" },
{ "22", "22" },
// Invalid values revert to expected
{ "abc", "0" },
{ "+abc", "0" },
{ "-abc", "0" },
});
}
[Test, ChildProcessTest]
public async Task PositiveDoubleFieldTest()
{
var theme = MatterHackers.MatterControl.AppContext.Theme;
var testField = new PositiveDoubleField(theme);
await ValidateAgainstValueMap(
testField,
theme,
(field) => (field.Content as ThemedNumberEdit).ActuallNumberEdit.Text,
new List<ValueMap>()
{
{ "0.12345", "0.12345" },
{ "1.2345", "1.2345" },
{ "12.345", "12.345" },
{ "12.7", "12.7" },
{ "+0.12345", "0.12345" },
{ "+1.2345", "1.2345" },
{ "+12.345", "12.345" },
{ "-0.12345", "0" },
{ "-1.2345", "0" },
{ "-12.345", "0" },
{ "-12.7", "0" },
{ "22", "22" },
// Invalid values revert to expected
{ "abc", "0" },
{ "+abc", "0" },
{ "-abc", "0" },
});
}
[Test, ChildProcessTest]
public async Task IntFieldTest()
{
var theme = MatterHackers.MatterControl.AppContext.Theme;
var testField = new IntField(theme);
await ValidateAgainstValueMap(
testField,
theme,
(field) => (field.Content as ThemedNumberEdit).ActuallNumberEdit.Text,
new List<ValueMap>()
{
{ "0.12345", "0" },
{ "1.2345", "1" },
{ "12.345", "12" },
{ "12.7", "12" }, // Floor not round?
{ "+0.12345", "0" },
{ "+1.2345", "1" },
{ "+12.345", "12" },
{ "-0.12345", "0" },
{ "-1.2345", "-1" },
{ "-12.345", "-12" },
{ "-12.7", "-12" }, // Floor not round?
{ "22", "22" },
// Invalid values revert to expected
{ "abc", "0" },
{ "+abc", "0" },
{ "-abc", "0" },
});
}
[Test, ChildProcessTest]
public async Task DoubleOrPercentFieldTest()
{
var theme = MatterHackers.MatterControl.AppContext.Theme;
var testField = new DoubleOrPercentField(theme);
await ValidateAgainstValueMap(
testField,
theme,
(field) => (field.Content as ThemedTextEditWidget).ActualTextEditWidget.Text,
new List<ValueMap>()
{
{ "0.12345", "0.12345" },
{ "0.12345%", "0.12345%" },
{ "1.2345", "1.2345" },
{ "1.2345%", "1.2345%" },
{ "12.345", "12.345" },
{ "12.345%", "12.345%" },
{ "12.7", "12.7" },
{ "12.7%", "12.7%" },
{ "+0.12345", "0.12345" },
{ "+0.12345%", "0.12345%" },
{ "+1.2345", "1.2345" },
{ "+1.2345%", "1.2345%" },
{ "+12.345", "12.345" },
{ "+12.345%", "12.345%" },
{ "-0.12345", "-0.12345" },
{ "-0.12345%", "-0.12345%" },
{ "-1.2345", "-1.2345" },
{ "-1.2345%", "-1.2345%" },
{ "-12.345", "-12.345" },
{ "-12.345%", "-12.345%" },
{ "12.7", "12.7" },
{ "12.7%", "12.7%" },
{ "22", "22" },
{ "22%", "22%" },
// Invalid values revert to expected
{ "abc", "0" },
{ "abc%", "0%" },
{ "+abc", "0" },
{ "+abc%", "0%" },
{ "-abc", "0" },
{ "-abc%", "0%" },
});
}
[Test, ChildProcessTest]
public async Task IntOrMmFieldTest()
{
var theme = MatterHackers.MatterControl.AppContext.Theme;
var testField = new IntOrMmField(theme);
await ValidateAgainstValueMap(
testField,
theme,
(field) => (field.Content as ThemedTextEditWidget).ActualTextEditWidget.Text,
new List<ValueMap>()
{
{ "0.12345", "0" },
{ "0.12345mm", "0.12345mm" },
{ "1.2345", "1" },
{ "1.2345mm", "1.2345mm" },
{ "12.345", "12" },
{ "12.345mm", "12.345mm" },
{ "12.7", "12" },
{ "12.7mm", "12.7mm" },
{ "+0.12345", "0" },
{ "+0.12345mm", "0.12345mm" },
{ "+1.2345", "1" },
{ "+1.2345mm", "1.2345mm" },
{ "+12.345", "12" },
{ "+12.345mm", "12.345mm" },
{ "-0.12345", "0" },
{ "-0.12345mm", "0mm" },
{ "-1.2345", "0" },
{ "-1.2345mm", "0mm" },
{ "-12.345", "0" },
{ "-12.345mm", "0mm" },
{ "12.7", "12" },
{ "12.7mm", "12.7mm" },
{ "22", "22" },
{ "22mm", "22mm" },
// Invalid values revert to expected
{ "abc", "0" },
{ "abcmm", "0mm" },
{ "+abc", "0" },
{ "+abcmm", "0mm" },
{ "-abc", "0" },
{ "-abcmm", "0mm" },
});
}
[Test, ChildProcessTest]
public void CorrectStyleForSettingsRow()
{
var settings = new PrinterSettings();
var printer = new PrinterConfig(settings);
settings.OemLayer = new PrinterSettingsLayer();
settings.QualityLayer = new PrinterSettingsLayer();
settings.MaterialLayer = new PrinterSettingsLayer();
Assert.AreEqual(0, settings.UserLayer.Count);
var theme = new ThemeConfig();
var settingsContext = new SettingsContext(printer, null, NamedSettingsLayers.All);
var key = SettingsKey.layer_height;
void TestStyle(Color color, bool restoreButton)
{
var data = SliceSettingsRow.GetStyleData(printer, theme, settingsContext, key, true);
Assert.AreEqual(color, data.highlightColor);
Assert.AreEqual(restoreButton, data.showRestoreButton);
}
// make sure all the colors are different
Assert.AreNotEqual(Color.Transparent, theme.PresetColors.MaterialPreset);
Assert.AreNotEqual(Color.Transparent, theme.PresetColors.QualityPreset);
Assert.AreNotEqual(theme.PresetColors.MaterialPreset, theme.PresetColors.QualityPreset);
Assert.AreNotEqual(theme.PresetColors.MaterialPreset, theme.PresetColors.UserOverride);
Assert.AreNotEqual(theme.PresetColors.QualityPreset, theme.PresetColors.UserOverride);
// nothing set no override
TestStyle(Color.Transparent, false);
// user override
settings.UserLayer[key] = "123";
TestStyle(theme.PresetColors.UserOverride, true);
settings.UserLayer.Remove(key);
// Quality override
settings.QualityLayer[key] = "123";
TestStyle(theme.PresetColors.QualityPreset, false);
settings.QualityLayer.Remove(key);
// Material override
settings.MaterialLayer[key] = "123";
TestStyle(theme.PresetColors.MaterialPreset, false);
settings.MaterialLayer.Remove(key);
// user override that is the same as the default
settings.UserLayer[key] = settings.BaseLayer[key];
TestStyle(Color.Transparent, false);
settings.UserLayer.Remove(key);
// Quality override same as default
settings.QualityLayer[key] = settings.BaseLayer[key];
TestStyle(theme.PresetColors.QualityPreset, false);
settings.QualityLayer.Remove(key);
// Material override same as default
settings.MaterialLayer[key] = settings.BaseLayer[key];
TestStyle(theme.PresetColors.MaterialPreset, false);
settings.MaterialLayer.Remove(key);
}
[Test, ChildProcessTest]
public void RightClickMenuWorksOnSliceSettings()
{
PrinterSettings.SliceEngines["MatterSlice"] = new EngineMappingsMatterSlice();
Clipboard.SetSystemClipboard(new SimulatedClipboard());
var systemWindow = new SystemWindow(800, 600)
{
Name = "Main Window",
};
InternalTextEditWidget.AddTextWidgetRightClickMenu(ApplicationController.Instance.MenuTheme);
AutomationRunner.TimeToMoveMouse = .1;
var container = new FlowLayoutWidget(FlowDirection.TopToBottom)
{
HAnchor = HAnchor.Stretch,
VAnchor = VAnchor.Fit,
};
systemWindow.AddChild(container);
var theme = ApplicationController.Instance.Theme;
var settings = new PrinterSettings();
settings.SetValue(SettingsKey.printer_name, "Test Name");
settings.SetValue(SettingsKey.start_gcode, "Test GCode");
var printer = new PrinterConfig(settings);
var settingsContext = new SettingsContext(printer, null, NamedSettingsLayers.All);
var testData = new (string setting, string paste, bool selectsOnFocus, string postCutAll)[]
{
(SettingsKey.layer_height, "987.654", true, "0"),
(SettingsKey.start_gcode, "Paste text", false, ""),
(SettingsKey.printer_name, "three word name", true, ""),
};
int tabIndex = 0;
var fields = new Dictionary<string, GuiWidget>();
for (int i = 0; i < testData.Length; i++)
{
var settingsData = PrinterSettings.SettingsData[testData[i].setting];
var itemRow = SliceSettingsTabView.CreateItemRow(settingsData,
settingsContext,
printer,
theme,
ref tabIndex);
fields[testData[i].setting] = itemRow;
container.AddChild(itemRow);
}
systemWindow.RunTest(testRunner =>
{
void RunSingeTest(string setting, string pastText, bool selectsOnFocus, string postCutAll)
{
var textWidget = fields[setting].Descendants<InternalTextEditWidget>().First();
// no selection to start
Assert.IsTrue(string.IsNullOrEmpty(textWidget.Selection), "Should not have selection");
// select all and ensure everything is selected
testRunner.RightClickByName(GetSliceSettingsField(setting));
if (selectsOnFocus)
{
testRunner.Assert(() => !string.IsNullOrEmpty(textWidget.Selection), "Should have selection");
}
else
{
testRunner.Assert(() => string.IsNullOrEmpty(textWidget.Selection), "Should not have selection");
}
testRunner
.ClickByName("Select All Menu Item")
.Assert(() => textWidget.Selection == textWidget.Text, "Everything is selected");
// make sure there is no text in the copy buffer
Clipboard.Instance.SetText("");
// copy text out and make sure we get it
testRunner
.RightClickByName(GetSliceSettingsField(setting))
.Assert(() => textWidget.Selection == textWidget.Text, "Selection remains after right click")
.ClickByName("Copy Menu Item")
.Assert(() => Clipboard.Instance.GetText() == textWidget.Text, "Copied everything");
// past in text and make sure it changed
Clipboard.Instance.SetText(pastText);
testRunner
.RightClickByName(GetSliceSettingsField(setting))
.ClickByName("Paste Menu Item")
.Assert(() => pastText == textWidget.Text, "Pasted everything");
// make sure we lose selection if we click off the menu
// cut works
testRunner
.RightClickByName(GetSliceSettingsField(setting))
.ClickByName("Select All Menu Item")
.Assert(() => textWidget.Selection == textWidget.Text, "Everything is selected");
testRunner
.RightClickByName(GetSliceSettingsField(setting))
.Assert(() => textWidget.Selection == textWidget.Text, "Selection remains after right click");
var preCutText = textWidget.Text;
testRunner
.ClickByName("Cut Menu Item")
.ClickByName("Main Window") // loose the selection
.Assert(() => postCutAll == textWidget.Text, "Cut everything")
.Assert(() => preCutText == Clipboard.Instance.GetText(), "Cut everything");
}
// testRunner.Delay(2000);
for (int i = 0; i < testData.Length; i++)
{
var data = testData[i];
RunSingeTest(data.setting, data.paste, data.selectsOnFocus, data.postCutAll);
}
return Task.CompletedTask;
},
2000);
}
private string GetSliceSettingsField(string sliceSettingsKey)
{
var settingData = PrinterSettings.SettingsData[sliceSettingsKey];
return $"{settingData.PresentationName} Field";
}
[Test, ChildProcessTest]
public async Task ComPortFieldTest()
{
FrostedSerialPort.MockPortsForTest = true;
var theme = new ThemeConfig();
var field = new ComPortField(new PrinterConfig(PrinterSettings.Empty), theme);
await ValidateAgainstValueMap(
field,
theme,
(f) => (f.Content.Children<DropDownList>().FirstOrDefault() as DropDownList).SelectedLabel,
new List<ValueMap>()
{
{ "COM-TestA", "COM-TestA" },
{ "COM-TestB", "COM-TestB" },
{ "COM-TestC", "COM-TestC" },
{ "COM-Test0", "COM-Test0" },
{ "COM-Test1", "COM-Test1" },
});
}
[TearDown]
public void TearDown()
{
FrostedSerialPort.MockPortsForTest = false;
}
[Test, Ignore("Not Implemented")]
public void CheckboxFieldTest()
{
Assert.Fail();
}
[Test, Ignore("Not Implemented")]
public void MaterialIndexFieldTest()
{
Assert.Fail();
}
[Test, Ignore("Not Implemented")]
public void ColorFieldTest()
{
Assert.Fail();
}
[Test, Ignore("Not Implemented")]
public void ChildrenSelectorListFieldTest()
{
Assert.Fail();
}
[Test, Ignore("Not Implemented")]
public void ToggleboxFieldTest()
{
Assert.Fail();
}
[Test, ChildProcessTest]
public async Task MultilineStringFieldTest()
{
var theme = MatterHackers.MatterControl.AppContext.Theme;
var testField = new MultilineStringField(theme);
await ValidateAgainstValueMap(
testField,
theme,
(field) => (field.Content as ThemedTextEditWidget).ActualTextEditWidget.Text,
new List<ValueMap>()
{
{ "0.12345", "0.12345" },
{ "1.2345", "1.2345" },
{ "12.345", "12.345" },
{ "12.7", "12.7" },
{ "+0.12345", "+0.12345" },
{ "+1.2345", "+1.2345" },
{ "+12.345", "+12.345" },
{ "-0.12345", "-0.12345" },
{ "-1.2345", "-1.2345" },
{ "-12.345", "-12.345" },
{ "12.7", "12.7" },
{ "22", "22" },
{ "abc", "abc" },
{ "+abc", "+abc" },
{ "-abc", "-abc" },
{ "-abc\nline2", "-abc\nline2" },
});
}
[Test, ChildProcessTest]
public async Task Vector2FieldTest()
{
var theme = MatterHackers.MatterControl.AppContext.Theme;
var testField = new Vector2Field(theme);
await ValidateAgainstValueMap(
testField,
theme,
(field) =>
{
return string.Join(",", field.Content.Children.OfType<ThemedNumberEdit>().Select(w => w.ActuallNumberEdit.Text).ToArray());
},
new List<ValueMap>()
{
{ "0.1,0.2", "0.1,0.2" },
{ "1,2", "1,2" },
{ ",2", "0,2" }, // Empty components should revert to 0s
{ "x,2", "0,2" }, // Non-numeric components should revert to 0s
{ "2", "0,0" }, // Non-vector4 csv should revert to Vector4.Zero
});
}
[Test, ChildProcessTest]
public async Task Vector3FieldTest()
{
var theme = MatterHackers.MatterControl.AppContext.Theme;
var testField = new Vector3Field(theme);
await ValidateAgainstValueMap(
testField,
theme,
(field) =>
{
return string.Join(",", field.Content.Children.OfType<ThemedNumberEdit>().Select(w => w.ActuallNumberEdit.Text).ToArray());
},
new List<ValueMap>()
{
{ "0.1,0.2,0.3", "0.1,0.2,0.3" },
{ "1,2,3", "1,2,3" },
{ ",2,", "0,2,0" }, // Empty components should revert to 0s
{ "x,2,y", "0,2,0" }, // Non-numeric components should revert to 0s
{ ",2", "0,0,0" }, // Non-vector4 csv should revert to Vector4.Zero
});
}
[Test, ChildProcessTest]
public async Task Vector4FieldTest()
{
var theme = MatterHackers.MatterControl.AppContext.Theme;
Vector4Field.VectorXYZWEditWidth = 50;
var testField = new Vector4Field(theme);
await ValidateAgainstValueMap(
testField,
theme,
(field) =>
{
return string.Join(",", field.Content.Children.OfType<ThemedNumberEdit>().Select(w => w.ActuallNumberEdit.Text).ToArray());
},
new List<ValueMap>()
{
{ "0.1,0.2,0.3,0.4", "0.1,0.2,0.3,0.4" },
{ "1,2,3,4", "1,2,3,4" },
{ ",2,,4", "0,2,0,4" }, // Empty components should revert to 0s
{ "x,2,y,4", "0,2,0,4" }, // Non-numeric components should revert to 0s
{ ",2,", "0,0,0,0" }, // Non-vector4 csv should revert to Vector4.Zero
});
}
[Test, ChildProcessTest]
public async Task BoundsFieldTest()
{
var theme = MatterHackers.MatterControl.AppContext.Theme;
var testField = new BoundsField(theme);
await ValidateAgainstValueMap(
testField,
theme,
(field) =>
{
return string.Join(",", field.Content.Children.OfType<ThemedNumberEdit>().Select(w => w.ActuallNumberEdit.Text).ToArray());
},
new List<ValueMap>()
{
{ "0.1,0.2,0.3,0.4", "0.1,0.2,0.3,0.4" },
{ "1,2,3,4", "1,2,3,4" },
{ ",2,,4", "0,2,0,4" }, // Empty components should revert to 0s
{ "x,2,y,4", "0,2,0,4" }, // Non-numeric components should revert to 0s
{ ",2,", "0,0,0,0" }, // Non-vector4 csv should revert to Vector4.Zero
});
}
[Test, Ignore("Not Implemented")]
public void ListFieldTest()
{
Assert.Fail();
}
[Test, ChildProcessTest]
public async Task ExtruderOffsetFieldTest()
{
var theme = MatterHackers.MatterControl.AppContext.Theme;
var printer = new PrinterConfig(new PrinterSettings());
var testField = new ExtruderOffsetField(printer,
new SettingsContext(printer, null, NamedSettingsLayers.All),
theme);
await ValidateAgainstValueMap(
testField,
theme,
(field) =>
{
return string.Join("x", field.Content.Descendants<ThemedNumberEdit>().Select(w => w.ActuallNumberEdit.Text).ToArray());
},
new List<ValueMap>()
{
{ "0x0x0", "0x0x0" },
{ "0x0", "0x0x0" }, // we store 3 offsets now, when we see 2 we should make 3
// {"", "0x0x0"}, // no values should become 0s
});
}
public class ValueMap
{
[DebuggerStepThrough]
public ValueMap(string input, string expected)
{
this.InputValue = input;
this.ExpectedValue = expected;
}
public string InputValue { get; }
public string ExpectedValue { get; }
}
/// <summary>
/// Take a UIField, a delegate to resolve the UI widget value and a map of input->expected values and validates the results for a given field
/// </summary>
/// <param name="field"></param>
/// <param name="collectValueFromWidget">A delegate to resolve the currently displayed widget value</param>
/// <param name="valuesMap">A map of input to expected values</param>
/// <returns></returns>
public static Task ValidateAgainstValueMap(UIField field, ThemeConfig theme, Func<UIField, string> collectValueFromWidget, IEnumerable<ValueMap> valuesMap)
{
// *************** Enable to investigate/debug/develop new/existing tests ************************
bool investigateDebugTests = false;
var perItemDelay = investigateDebugTests ? 1000 : 0;
var testsWindow = new UIFieldTestWindow(500, 200, field, theme);
return testsWindow.RunTest((testRunner) =>
{
foreach (var item in valuesMap)
{
testsWindow.SetAndValidateValues(item.ExpectedValue, item.InputValue, collectValueFromWidget, perItemDelay);
}
return Task.CompletedTask;
}, 30);
}
}
}

View file

@ -0,0 +1,77 @@
/*
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.IO;
using System.Threading;
using MatterHackers.Agg.Platform;
using MatterHackers.MatterControl.Tests.Automation;
using MatterHackers.PolygonMesh;
using MatterHackers.PolygonMesh.Processors;
using MatterHackers.VectorMath;
using NUnit.Framework;
namespace MatterHackers.MatterControl.Slicing.Tests
{
[TestFixture, Category("MatterControl.Slicing")]
public class SliceLayersTests
{
//[Test]
public void SliceLayersGeneratingCorrectSegments()
{
// TODO: Make tests work on Mac as well as Windows
if (AggContext.OperatingSystem == OSType.Mac)
{
return;
}
string meshFileName = Path.Combine(MatterControlUtilities.RootPath, "Tests", "TestData", "TestMeshes", "SliceLayers", "Box20x20x10.stl");
Mesh cubeMesh = StlProcessing.Load(meshFileName, CancellationToken.None);
AxisAlignedBoundingBox bounds = cubeMesh.GetAxisAlignedBoundingBox();
Assert.IsTrue(bounds.ZSize == 10);
//var alllayers = slicelayers.getperimetersforalllayers(cubemesh, .2, .2);
//assert.istrue(alllayers.count == 50);
//foreach (slicelayer layer in alllayers)
//{
// assert.istrue(layer.unorderedsegments.count == 8);
// // work in progress
// //assert.istrue(layer.perimeters.count == 1);
// //assert.istrue(layer.perimeters[0].count == 8);
//}
//alllayers = slicelayers.getperimetersforalllayers(cubemesh, .2, .1);
//Assert.IsTrue(allLayers.Count == 99);
}
}
}

View file

@ -0,0 +1,87 @@
/*
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 NUnit.Framework;
namespace MatterHackers.MatterControl.SlicerConfiguration.Tests
{
[TestFixture, Category("MatterControl.SlicerConfiguration")]
public class SliceMappingTests
{
[Test]
public void AsPercentOfReferenceOrDirectTests()
{
#if !__ANDROID__
// Set the static data to point to the directory of MatterControl
StaticData.Instance = new MatterHackers.Agg.FileSystemStaticData(Path.Combine("..", "..", "..", "..", "StaticData"));
var classicProfile = new ClassicSqlitePrinterProfiles();
// dirrect values work
{
classicProfile.SaveValue("primary", "1", 0);
classicProfile.SaveValue("reference", "10", 0);
AsPercentOfReferenceOrDirect mapper = new AsPercentOfReferenceOrDirect("primary", "notused", "reference");
Assert.IsTrue(mapper.Value == "1");
// and also scaled
AsPercentOfReferenceOrDirect mapper2 = new AsPercentOfReferenceOrDirect("primary", "notused", "reference", 1000);
Assert.IsTrue(mapper2.Value == "1000");
}
// % reference values work
{
classicProfile.SaveValue("primary", "13%", 0);
classicProfile.SaveValue("reference", "100", 0);
AsPercentOfReferenceOrDirect mapper = new AsPercentOfReferenceOrDirect("primary", "notused", "reference");
Assert.IsTrue(mapper.Value == "13");
// and also scaled
AsPercentOfReferenceOrDirect mapper2 = new AsPercentOfReferenceOrDirect("primary", "notused", "reference", 1000);
Assert.IsTrue(mapper2.Value == "13000");
}
// and also check for 0
{
classicProfile.SaveValue("primary", "0", 0);
classicProfile.SaveValue("reference", "100", 0);
AsPercentOfReferenceOrDirect mapper = new AsPercentOfReferenceOrDirect("primary", "notused", "reference");
Assert.IsTrue(mapper.Value == "100");
// and also scaled
AsPercentOfReferenceOrDirect mapper2 = new AsPercentOfReferenceOrDirect("primary", "notused", "reference", 1000);
Assert.IsTrue(mapper2.Value == "100000");
}
#endif
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,622 @@
/*
Copyright (c) 2019, 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.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using MatterControl.Printing;
using MatterHackers.Agg;
using MatterHackers.Agg.Platform;
using MatterHackers.MatterControl;
using MatterHackers.MatterControl.PrinterCommunication;
using MatterHackers.MatterControl.SlicerConfiguration;
using MatterHackers.MatterControl.Tests.Automation;
using MatterHackers.PrinterEmulator;
using MatterHackers.SerialPortCommunication.FrostedSerial;
using MatterHackers.VectorMath;
using NUnit.Framework;
using TestInvoker;
namespace MatterControl.Tests.MatterControl.ToolChanges
{
[TestFixture, Category("GCodeStream"), Parallelizable(ParallelScope.Children)]
public class ToolChangeTests
{
[SetUp]
public void TestSetup()
{
StaticData.RootPath = MatterControlUtilities.StaticDataPath;
MatterControlUtilities.OverrideAppDataLocation(MatterControlUtilities.RootPath);
}
[Test, ChildProcessTest]
public async Task ToolChangeNoHeat()
{
var printer = ToolChangeTests.CreatePrinter();
// validate that no heater is heated at anytime during the print
printer.Connection.HotendTargetTemperatureChanged += (s, extruderIndex) =>
{
if (printer.Connection.GetTargetHotendTemperature(extruderIndex) > 0)
{
Assert.Fail("No hotend should ever change temp during this test.");
}
};
// Collect gcode sent through stream processors
var sentLines = await printer.RunSimulatedPrint(
@"T0
; send some movement commands with tool switching
; the printer is moving normally
G1 X10 Y10 Z10 E0 F2500
T1
G1 X10 Y10 Z10 E0
T0
G1 X10 Y10 Z10 E0");
// Validate
var expectedLines = new string[]
{
"M114", // initial position request
"T0", // initial tool assignment (part of starting a default print)
"M114", // we always ask position after tool assignment
"G1 X10 Y10 Z10 F2500", // go to the position requested
"G1 Y111", // the pre switch T1 code
"M114", // always sent after a ; NO_PROCESSING command
"T1",
"M114", // after a tool change we inject an M114
"G1 Y222", // the post switch T1 code
"M114", // always sent after a ; NO_PROCESSING command
"G1 X9 Y8 F3000", // the destination position with consideration of T1 offset
"G1 Z7 F315", // we set xy than z, so this is the z
"G1 F2500", // we then reset the F after the pre and post gcode run
"G1 X111", // pre T0 code
"M114", // always sent after a ; NO_PROCESSING command
"M104 T1 S0",
"T1",
"M114",
"T0",
"M114", // always send after switch
"G1 X222", // post switch T0 code
"M114", // always sent after a ; NO_PROCESSING command
"G1 X10 Y10 F3000", // return to extruder position
"G1 Z10 F315",
"G1 F2500",
};
Assert.AreEqual(expectedLines, sentLines);
}
// A test that proves that: T0, no move, T1, T0, move does not send switch extruder gcode
[Test, ChildProcessTest]
public async Task NoToolChangeIfNoMove()
{
var printer = ToolChangeTests.CreatePrinter();
// Collect gcode sent through stream processors
var sentLines = await printer.RunSimulatedPrint(
@"T0
; send some movement commands with tool switching
; the printer is moving normally
G1 X10 Y10 Z10 E0 F2500
T1
T0
G1 X11 Y11 Z11 E0 F2500");
// Validate
var expectedLines = new string[]
{
"M114", // initial position request
"T0", // initial tool assignment (part of starting a default print)
"M114", // we always ask position after tool assignment
"G1 X10 Y10 Z10 F2500", // go to the position requested
"G1 X11 Y11 Z11", // go to the position requested
};
Console.WriteLine("===");
Console.WriteLine(String.Join("\n", sentLines));
Console.WriteLine("===");
Console.WriteLine(String.Join("\n", expectedLines));
Assert.That(sentLines, Is.EqualTo(expectedLines).NoClip);
}
// A test that proves that: T0, no move, T1, temp set, T0, move does not send switch extruder gcode
// but there is the correct extruder set, T1, then temp, than T0
[Test, ChildProcessTest]
public async Task ToolChangeTempSetWithNoMove()
{
var printer = ToolChangeTests.CreatePrinter();
// Collect gcode sent through stream processors
var sentLines = await printer.RunSimulatedPrint(
@"T0
; send some movement commands with tool switching
; the printer is moving normally
G1 X10 Y10 Z10 E0 F2500
T1
M104 S100
T0
G1 X11 Y11 Z11 E0 F2500");
// Validate
var expectedLines = new string[]
{
"M114", // initial position request
"T0", // initial tool assignment (part of starting a default print)
"M114", // we always ask position after tool assignment
"G1 X10 Y10 Z10 F2500", // go to the position requested
"M104 T1 S100", // set the temp for T1
"T0", // smoothie command to ensure still on T0 after temp set
"M114", // always ask position after T
"G1 X11 Y11 Z11", // go to the position requested
};
Assert.AreEqual(expectedLines, sentLines);
}
[Test, ChildProcessTest]
public async Task SetTempDirectSmoothie()
{
var printer = ToolChangeTests.CreatePrinter();
// Collect gcode sent through stream processors
var sentLines = await printer.RunSimulatedPrint(
@"T0
; send some movement commands with tool switching
; the printer is moving normally
G1 X10 Y10 Z10 E0 F2500
M104 S100 T1
G1 X11 Y11 Z11 E0 F2500");
// Validate
var expectedLines = new string[]
{
"M114", // initial position request
"T0", // initial tool assignment (part of starting a default print)
"M114", // we always ask position after tool assignment
"G1 X10 Y10 Z10 F2500", // go to the position requested
"M104 T1 S100", // set the temp for T1
"T0", // smoothie command to ensure still on T0 after temp set
"M114", // always ask position after T
"G1 X11 Y11 Z11", // go to the position requested
};
Assert.AreEqual(expectedLines, sentLines);
}
[Test, ChildProcessTest]
public async Task SetTempDirectMarlin()
{
var printer = ToolChangeTests.CreatePrinter();
printer.Settings.SetValue(SettingsKey.firmware_type, "MARLIN");
// Collect gcode sent through stream processors
var sentLines = await printer.RunSimulatedPrint(
@"T0
; send some movement commands with tool switching
; the printer is moving normally
G1 X10 Y10 Z10 E0 F2500
M104 S100 T1
G1 X11 Y11 Z11 E0 F2500");
// Validate
var expectedLines = new string[]
{
"M114", // initial position request
"T0", // initial tool assignment (part of starting a default print)
"M114", // we always ask position after tool assignment
"G1 X10 Y10 Z10 F2500", // go to the position requested
"M104 T1 S100", // set the temp for T1
"G1 X11 Y11 Z11", // go to the position requested
};
Assert.AreEqual(expectedLines, sentLines);
}
// A test that proves that: T0, no move, T1, extrude, T0, move does not send switch extruder gcode
// but does switch to and back for extrude
[Test, ChildProcessTest]
public async Task NoMoveOnToolChangeButWithExtrude()
{
var printer = ToolChangeTests.CreatePrinter();
// Collect gcode sent through stream processors
var sentLines = await printer.RunSimulatedPrint(
@"T0
; send some movement commands with tool switching
; the printer is moving normally
G1 X10 Y10 Z10 E0 F2500
T1
G1 E10
G1 E20
T0
G1 E30
G1 X11 Y11 Z11 E30 F2500");
// Validate
var expectedLines = new string[]
{
"M114", // initial position request
"T0", // initial tool assignment (part of starting a default print)
"M114", // we always ask position after tool assignment
"G1 X10 Y10 Z10 F2500", // go to the position requested
"T1", // switch to do extrusion
"G92 E0", // set the extrusion after switch (for smoothie)
"G1 E10", // the first extrusion on T1
"T0", // switch back to T0
"G92 E10", // set the extrusion after switch (for smoothie)
"M114", // 10
"T1",
"G92 E10", // set the extrusion after switch (for smoothie)
"G1 E20", // a second extrusion without changing back to T0
"T0", // the 'no move' switch back to T0
"G92 E20", // set the extrusion after switch (for smoothie)
"M114",
"G1 E30", // extrude on T0
"G1 X11 Y11 Z11", // go to the position requested
};
Assert.AreEqual(expectedLines, sentLines);
}
[Test, ChildProcessTest]
public async Task MarlinPrintingToolChanges()
{
var printer = ToolChangeTests.CreatePrinter();
printer.Settings.SetValue(SettingsKey.firmware_type, "MARLIN");
printer.Settings.SetValue(SettingsKey.before_toolchange_gcode, "");
printer.Settings.SetValue(SettingsKey.before_toolchange_gcode_1, "");
printer.Settings.SetValue(SettingsKey.toolchange_gcode, "");
printer.Settings.SetValue(SettingsKey.toolchange_gcode_1, "");
// Collect gcode sent through stream processors
var sentLines = await printer.RunSimulatedPrint(
@"G0 X93.444 Y147.916
G1 E47.2643 F1200 ; retract
G92 E0 ; reset extrusion
T1 ; switch extruder
G1 F1200 E10
G1 F2400 X93.411 Y147.936 E10.00076
G1 X93.382 Y147.949 E10.00138");
// Validate
var expectedLines = new string[]
{
"M114", // we start with a position request
"G1 X93.44 Y147.92", // goto the first position
"G1 E47.264 F1200", // do the retract
"G92 E0", // reset the extrusion
"T1", // switch to T1
"G1 F1200 E10",
"M114",
"T1",
"M114",
"G1 X92.41 Y145.94 F3000",
"G1 F315",
"G1 F1200",
"G1 E10.001 F2400",
"G1 X92.38 Y145.95",
};
foreach (var line in sentLines)
{
Debug.WriteLine($"\"{line}\",");
}
Assert.AreEqual(expectedLines, sentLines);
}
[Test, ChildProcessTest]
public async Task ToolChangeTempAndSwitch()
{
var printer = ToolChangeTests.CreatePrinter();
// Collect gcode sent through stream processors
var sentLines = await printer.RunSimulatedPrint(
@"T0
; tell the printer to heat up
M104 T1 S240 ; start with T0 to test smoothie temp change code
M104 T0 S230
; send some movement commands with tool switching
; the printer is moving normally
G1 X10 Y10 Z10 E0 F2500
T1
G1 X10 Y10 Z10 E0
T0
G1 X10 Y10 Z10 E0");
// Validate
var expectedLines = new string[]
{
"M114",
"T0",
"M114",
"M104 T1 S240", // initial heating
"T0",
"M114",
"M104 T0 S230",
"G1 X10 Y10 Z10 F2500",
"G1 Y111",
"M114",
"T1",
"M114",
"M104 T1 S240",
"G1 Y222",
"M114",
"G1 X9 Y8 F3000",
"G1 Z7 F315",
"G1 F2500",
"G1 X111",
"M114",
"M104 T1 S0",
"T1",
"M114",
"T0",
"M114",
"G1 X222",
"M114",
"G1 X10 Y10 F3000",
"G1 Z10 F315",
"G1 F2500",
};
Assert.AreEqual(expectedLines, sentLines);
}
[Test, ChildProcessTest]
public async Task ToolChangeWithCoolDown()
{
var printer = ToolChangeTests.CreatePrinter();
// set cool down temp and time so the extruder will do a cool down on switch
printer.Settings.SetValue(SettingsKey.inactive_cool_down, $"{30}");
printer.Settings.SetValue(SettingsKey.seconds_to_reheat, $"{1}");
// Collect gcode sent through stream processors
var sentLines = await printer.RunSimulatedPrint(
@"T0
; tell the printer to heat up
M104 T1 S240 ; start with T0 to test smoothie temp change code
M104 T0 S230
; send some movement commands with tool switching
; the printer is moving normally
G1 X10 Y10 Z10 E0 F2500
T1
G1 X20 Y10 Z10 E10 F10 ; a long move with extrusion, will need to cool T0
T0
G1 X30 Y10 Z10 E10 F10 ; a long move with extrusion, will need to cool T1
T1
G1 X10 Y10 Z10 E0");
// Validate
var expectedLines = new string[]
{
"M114",
"T0",
"M114",
"M104 T1 S240", // T1 to 240
"T0",
"M114",
"M104 T1 S240",
"T0",
"M114",
"M104 T0 S230", // T0 to 230
"G1 X10 Y10 Z10 F2500",
"G1 Y111",
"M114",
"M104 T0 S200", // T0 to cool down temp
"T0",
"M114",
"T1",
"M114",
"M104 T1 S240",
"G1 Y222",
"M114", // 20
"G1 X19 Y8 F3000",
"G1 Z7 F315",
"G1 F2500",
"G1 E10 F10",
"G1 X111",
"M114",
"M104 T1 S210",
"T1",
"M114",
"T0",
"M114",
"M104 T0 S230",
"G1 X222",
"M114",
"G1 X30 Y10 F3000",
"G1 Z10 F315",
"G1 F10",
"G1 E10",
"G1 Y111",
"M114",
"M104 T0 S0",
"T0",
"M114",
"T1",
"M114",
"M104 T1 S240", // T1 back up to 240
"G1 Y222",
"M114",
"G1 X9 Y8 F3000",
"G1 Z7 F315",
"G1 F10",
"G1 E0",
};
Assert.AreEqual(expectedLines, sentLines);
}
[Test, ChildProcessTest]
public async Task ToolChangeHeatOnlyT0()
{
var printer = ToolChangeTests.CreatePrinter();
// register to make sure that T0 is heated (only once) and T1 is not heated
printer.Connection.HotendTargetTemperatureChanged += (s, extruderIndex) =>
{
Assert.AreEqual(0, printer.Connection.GetTargetHotendTemperature(1));
};
await printer.RunSimulatedPrint(
@"T0
; tell the printer to heat up
M104 T0 S230
; send some movement commands with tool switching
; the printer is moving normally
G1 X10 Y10 Z10 E0 F2500
T1
G1 X10 Y10 Z10 E0
T0
G1 X10 Y10 Z10 E0");
// now do the same thing with a long enough print to cause
// cooling and heating
}
[Test, ChildProcessTest]
public async Task ToolChangeHeatOnlyT1()
{
var printer = ToolChangeTests.CreatePrinter();
// register to make sure that T0 is heated (only once) and T1 is not heated
printer.Connection.HotendTargetTemperatureChanged += (s, extruderIndex) =>
{
Assert.AreEqual(0, printer.Connection.GetTargetHotendTemperature(0));
};
await printer.RunSimulatedPrint(
@"T0
; tell the printer to heat up
M104 T1 S230
; send some movement commands with tool switching
; the printer is moving normally
G1 X10 Y10 Z10 E0 F2500
T1
G1 X10 Y10 Z10 E0
T0
G1 X10 Y10 Z10 E0");
// now do the same thing with a long enough print to cause
// cooling and heating
}
private static PrinterConfig CreatePrinter()
{
// this is the pause and resume from the Eris
var printer = new PrinterConfig(new PrinterSettings());
// setup for dual extrusion
printer.Settings.SetValue(SettingsKey.extruder_count, "2");
printer.Settings.SetValue(SettingsKey.enable_line_splitting, "0");
printer.Settings.SetValue(SettingsKey.before_toolchange_gcode, "G1 X111 ; NO_PROCESSING");
printer.Settings.SetValue(SettingsKey.toolchange_gcode, "G1 X222 ; NO_PROCESSING");
printer.Settings.SetValue(SettingsKey.before_toolchange_gcode_1, "G1 Y111 ; NO_PROCESSING");
printer.Settings.SetValue(SettingsKey.toolchange_gcode_1, "G1 Y222 ; NO_PROCESSING");
// set some data for T1
printer.Settings.Helpers.SetExtruderOffset(1, new Vector3(1, 2, 3));
return printer;
}
}
public static class ExtensionMethods
{
public static Task<List<string>> RunSimulatedPrint(this PrinterConfig printer, string[] inputGCode)
{
var inputStream = new MemoryStream(Encoding.ASCII.GetBytes(string.Join("\n", inputGCode)));
return RunSimulatedPrint(printer, inputStream);
}
public static Task<List<string>> RunSimulatedPrint(this PrinterConfig printer, string gcode)
{
var inputStream = new MemoryStream(Encoding.ASCII.GetBytes(gcode));
return RunSimulatedPrint(printer, inputStream);
}
public static async Task<List<string>> RunSimulatedPrint(this PrinterConfig printer, Stream inputStream)
{
// set up our serial port finding
FrostedSerialPortFactory.GetPlatformSerialPort = (_) =>
{
return new Emulator();
};
var sentLines = new List<string>();
// register to listen to the printer responses
printer.Connection.LineSent += (s, line) =>
{
if (printer.Connection.Printing)
{
sentLines.Add(line);
}
};
// set up the emulator
printer.Settings.SetValue($"{Environment.MachineName}_com_port", "Emulator");
// connect to the emulator
printer.Connection.Connect();
var timer = Stopwatch.StartNew();
// wait for the printer to be connected
while (!printer.Connection.IsConnected
&& timer.ElapsedMilliseconds < (1000 * 40))
{
Thread.Sleep(100);
}
// start a print
printer.Connection.CommunicationState = CommunicationStates.PreparingToPrint;
// NOTE: Test can sometimes fail due to resends if this happens too quickly, it seems.
// Extra commands were sent by: WriteRaw(allCheckSumLinesSent[currentLineIndexToSend++] + "\n", "resend");
Thread.Sleep(1000);
await printer.Connection.StartPrint(inputStream, null, null, PrinterConnection.PrintingModes.Normal);
// wait up to 40 seconds for the print to finish
timer = Stopwatch.StartNew();
while (printer.Connection.Printing
&& timer.ElapsedMilliseconds < (1000 * 40))
{
Thread.Sleep(100);
}
// Project to string without checksum which is not an M105 request
return sentLines.Select(l => GCodeFile.GetLineWithoutChecksum(l)).Where(l => !l.StartsWith("M105")).ToList();
}
}
}

View file

@ -0,0 +1,194 @@
/*
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.MatterControl;
using NUnit.Framework;
using System;
using System.IO;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Text;
using System.Linq;
using MatterHackers.Agg.UI;
using MatterHackers.MatterControl.Tests.Automation;
namespace MatterControl.Tests.MatterControl
{
[TestFixture]
public class TranslationsTests
{
// Culture-sensitive DateTime strings here.
[Test, Category("Translations"), SetCulture("")]
public void RelativeFriendlyDatesTest()
{
{
// May 28, 2016 at 3:13 pm
DateTime nowTime = new DateTime(2016, 05, 28, 15, 13, 37);
DateTime testTime = nowTime.AddMinutes(-63);
Assert.IsTrue(RelativeTime.GetTimeBlock(nowTime, testTime) == TimeBlock.Today);
Assert.IsTrue(RelativeTime.GetDetail(nowTime, testTime) == "2:10 PM");
testTime = nowTime.AddHours(-25);
Assert.IsTrue(RelativeTime.GetTimeBlock(nowTime, testTime) == TimeBlock.Yesterday);
Assert.IsTrue(RelativeTime.GetDetail(nowTime, testTime) == "2:13 PM");
testTime = nowTime.AddDays(-4);
Assert.IsTrue(RelativeTime.GetTimeBlock(nowTime, testTime) == TimeBlock.SameWeek);
Assert.IsTrue(RelativeTime.GetDetail(nowTime, testTime) == "Tuesday 3:13 PM");
testTime = nowTime.AddDays(-6);
Assert.IsTrue(RelativeTime.GetTimeBlock(nowTime, testTime) == TimeBlock.SameWeek);
Assert.IsTrue(RelativeTime.GetDetail(nowTime, testTime) == "Sunday 3:13 PM");
testTime = nowTime.AddDays(-7);
Assert.IsTrue(RelativeTime.GetTimeBlock(nowTime, testTime) == TimeBlock.SameMonth);
Assert.IsTrue(RelativeTime.GetDetail(nowTime, testTime) == "May 21, 3:13 PM");
testTime = nowTime.AddDays(-37);
Assert.IsTrue(RelativeTime.GetTimeBlock(nowTime, testTime) == TimeBlock.SameYear);
Assert.IsTrue(RelativeTime.GetDetail(nowTime, testTime) == "April 21, 3:13 PM");
testTime = nowTime.AddDays(-364);
Assert.IsTrue(RelativeTime.GetTimeBlock(nowTime, testTime) == TimeBlock.PastYear);
Assert.IsTrue(RelativeTime.GetDetail(nowTime, testTime) == "2015 May 30, 3:13 PM");
}
// make a grouped list
{
// May 28, 2016 at 3:13 pm
DateTime nowTime = new DateTime(2016, 05, 28, 15, 13, 37);
List<DateTime> allTimes = new List<DateTime>()
{
nowTime.AddMinutes(-63),
nowTime.AddMinutes(-82),
nowTime.AddHours(-25),
nowTime.AddHours(-31),
nowTime.AddDays(-4),
nowTime.AddDays(-6),
nowTime.AddDays(-7),
nowTime.AddDays(-37),
nowTime.AddDays(-364),
};
var orderedForDisplay = RelativeTime.GroupTimes(nowTime, allTimes);
Assert.IsTrue(orderedForDisplay.Count == 6);
Assert.IsTrue(orderedForDisplay[TimeBlock.Today].Count == 2);
Assert.IsTrue(orderedForDisplay[TimeBlock.Yesterday].Count == 2);
Assert.IsTrue(orderedForDisplay[TimeBlock.SameWeek].Count == 2);
Assert.IsTrue(orderedForDisplay[TimeBlock.SameMonth].Count == 1);
Assert.IsTrue(orderedForDisplay[TimeBlock.SameYear].Count == 1);
Assert.IsTrue(orderedForDisplay[TimeBlock.PastYear].Count == 1);
}
}
[Test, Category("Translations")]
public void EnglishLinesOnlyContainEnglishCharachters()
{
string fullPath = Path.Combine(MatterControlUtilities.StaticDataPath, "Translations");
foreach (string directory in Directory.GetDirectories(fullPath))
{
string fullPathToEachTranslation = Path.Combine(directory, "Translation.txt");
Console.Write(fullPathToEachTranslation);
readTranslationFile(fullPathToEachTranslation);
}
/*File.ReadAllLines(fullPath).Where(s => s.StartsWith("English:")).Select(s =>
{
return s.Replace("English:", "").Trim();
})
.Where(s =>
{
var items = s.ToCharArray().Select(c => (int)c);
var result1 = items.Where(i => i > 127);
var result2 = result1.Any();
return result2;
}).ToArray();//);*/
//checkForNonEnglishCharacters(fullPath);
}
public void readTranslationFile(string pathToTranslations)
{
bool hasInvalid;
foreach (string s in File.ReadAllLines(pathToTranslations))
{
var k = s;
if (k.StartsWith("English:"))
{
k = k.Replace("English:", "").Trim();
var chars = k.ToCharArray();
var ints = chars.Select(c => (int)c).ToArray();
hasInvalid = checkForInvalidCharacters(ints);
if (hasInvalid)
{
string result = hasInvalid.ToString();
string fullResult = String.Format("{0}: {1}", k, result);
Console.WriteLine(fullResult);
}
}
}
}
public bool checkForInvalidCharacters(int[] bytesInCharacter)
{
bool hasInvalidCharacter = false;
foreach (int i in bytesInCharacter)
{
if (i > 127)
{
hasInvalidCharacter = true;
return hasInvalidCharacter;
}
}
return hasInvalidCharacter;
}
}
}

View file

@ -0,0 +1,136 @@
/*
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.Reflection.Metadata.Ecma335;
using MatterHackers.Agg;
using MatterHackers.Agg.UI;
using MatterHackers.MatterControl.SlicerConfiguration;
using NUnit.Framework;
namespace MatterControl.Tests.MatterControl
{
public class UIFieldTestWindow : SystemWindow
{
public ThemedTextEditWidget ExpectedText { get; }
public ThemedTextEditWidget InputText { get; }
private UIField field;
public UIFieldTestWindow(int width, int height, UIField field, ThemeConfig theme)
: base(width, height)
{
this.BackgroundColor = new Color(56, 56, 56);
GuiWidget column, row;
double pixelWidth = 70;
// Store
this.field = field;
int tabIndex = 0;
// Initialize the field and store the generated content reference
field.Initialize(ref tabIndex);
GuiWidget widgetUnderTest = field.Content;
row = new FlowLayoutWidget
{
VAnchor = VAnchor.Center | VAnchor.Fit,
HAnchor = HAnchor.Center | HAnchor.Fit
};
this.AddChild(row);
column = new FlowLayoutWidget(FlowDirection.TopToBottom)
{
Margin = new BorderDouble(0, 10),
};
row.AddChild(column);
column.AddChild(new TextWidget("Input:", textColor: Color.White)
{
Margin = new BorderDouble(right: 10, bottom: 2),
});
this.InputText = new ThemedTextEditWidget("", theme, pixelWidth: pixelWidth)
{
Margin = new BorderDouble(right: 8)
};
column.AddChild(InputText);
column = new FlowLayoutWidget(FlowDirection.TopToBottom)
{
Margin = new BorderDouble(0, 10),
};
row.AddChild(column);
column.AddChild(new TextWidget("Expected:", textColor: Color.White)
{
Margin = new BorderDouble(right: 10, bottom: 2)
});
this.ExpectedText = new ThemedTextEditWidget("", theme, pixelWidth: pixelWidth)
{
Margin = new BorderDouble(right: 8)
};
column.AddChild(ExpectedText);
column = new FlowLayoutWidget(FlowDirection.TopToBottom)
{
Margin = new BorderDouble(0, 10),
};
row.AddChild(column);
column.AddChild(new TextWidget("Actual:", textColor: Color.White)
{
Margin = new BorderDouble(right: 10, bottom: 2)
});
column.AddChild(widgetUnderTest);
}
public void SetAndValidateValues(string expectedValue, string inputValue, Func<UIField, string> collectValueFromWidget, int delay = 200)
{
// Set expected and source
this.ExpectedText.Text = expectedValue;
this.InputText.Text = inputValue;
// Update field
field.SetValue(inputValue, false);
// Assert expected field value
Assert.AreEqual(expectedValue, field.Value);
// Assert expected widget value
Assert.AreEqual(expectedValue, collectValueFromWidget(field));
// Sleep
System.Threading.Thread.Sleep(delay);
}
}
}

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.Tests")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("MatterHackers, Inc.")]
[assembly: AssemblyProduct("MatterControl.Tests")]
[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("ecbee48f-eea8-4700-9635-6d8513f8ffcb")]
// 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.Tests": {
"commandName": "Project",
"nativeDebugging": true
}
}
}

View file

@ -0,0 +1,620 @@
/*
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 MatterHackers.Agg;
using MatterHackers.Agg.Platform;
using MatterHackers.DataConverters3D;
using MatterHackers.MatterControl;
using MatterHackers.MatterControl.PartPreviewWindow;
using MatterHackers.MatterControl.Tests.Automation;
using MatterHackers.VectorMath;
using NUnit.Framework;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using TestInvoker;
namespace MatterHackers.PolygonMesh.UnitTests
{
[TestFixture, Category("Agg.PolygonMesh"), Parallelizable(ParallelScope.Children)]
public class SceneTests
{
private readonly int BlueMaterialIndex = 6;
private readonly Matrix4X4 BlueMatrix = Matrix4X4.CreateTranslation(20, 0, 0);
private readonly PrintOutputTypes BlueOutputType = PrintOutputTypes.Solid;
private readonly int GreenMaterialIndex = 5;
private readonly Matrix4X4 GreenMatrix = Matrix4X4.CreateTranslation(15, 0, 0);
private readonly PrintOutputTypes GreenOutputType = PrintOutputTypes.Support;
private readonly int GroupMaterialIndex = 3;
private readonly Matrix4X4 GroupMatrix = Matrix4X4.Identity;
private readonly PrintOutputTypes GroupOutputType = PrintOutputTypes.Solid;
private readonly int RedMaterialIndex = 4;
private readonly Matrix4X4 RedMatrix = Matrix4X4.CreateTranslation(10, 0, 0);
private readonly PrintOutputTypes RedOutputType = PrintOutputTypes.Support;
private readonly int RootMaterialIndex = 1;
private readonly Matrix4X4 RootMatrix = Matrix4X4.Identity;
private readonly PrintOutputTypes RootOutputType = PrintOutputTypes.Solid;
private readonly int SuperGroupMaterialIndex = 2;
private readonly Matrix4X4 SuperGroupMatrix = Matrix4X4.CreateScale(2);
private readonly PrintOutputTypes SuperGroupOutputType = PrintOutputTypes.Solid;
public static string GetSceneTempPath(string folder)
{
string tempPath = Path.Combine(MatterControlUtilities.RootPath, "Tests", "temp", folder);
Directory.CreateDirectory(tempPath);
return tempPath;
}
[Test, ChildProcessTest]
public void AmfFilesSaveObjectProperties()
{
AssetObject3D.AssetManager = new AssetManager();
var scene = new InteractiveScene();
scene.Children.Add(new Object3D
{
Mesh = PlatonicSolids.CreateCube(20, 20, 20),
Name = "test1",
OutputType = PrintOutputTypes.Support,
Color = Color.Red,
MaterialIndex = 2,
});
scene.Children.Add(new Object3D
{
Mesh = PlatonicSolids.CreateCube(20, 20, 20),
Name = "test2",
Matrix = Matrix4X4.CreateTranslation(30, 0, 0)
});
string tempPath = GetSceneTempPath("temp_amf");
Object3D.AssetsPath = Path.Combine(tempPath, "Assets");
string filePath = Path.Combine(tempPath, "exportTest.amf");
scene.SetSelection(scene.Children.ToList());
AmfDocument.Save(scene.SelectedItem, filePath);
Assert.IsTrue(File.Exists(filePath));
IObject3D loadedItem = Object3D.Load(filePath, CancellationToken.None);
Assert.IsTrue(loadedItem.Children.Count == 2);
IObject3D item1 = loadedItem.Children.Last();
Assert.AreEqual("test1", item1.Name);
Assert.AreEqual(PrintOutputTypes.Support, item1.OutputType);
Assert.AreEqual(2, item1.MaterialIndex);
Assert.AreEqual(Color.Red, item1.Color);
Assert.AreEqual(12, item1.Mesh.Faces.Count);
var aabb1 = item1.GetAxisAlignedBoundingBox();
Assert.True(new AxisAlignedBoundingBox(-10, -10, -10, 10, 10, 10).Equals(aabb1, .001));
IObject3D item2 = loadedItem.Children.First();
Assert.AreEqual("test2", item2.Name);
Assert.AreEqual(Color.White, item2.Color);
Assert.AreEqual(12, item2.Mesh.Faces.Count);
var aabb2 = item2.GetAxisAlignedBoundingBox();
Assert.True(new AxisAlignedBoundingBox(20, -10, -10, 40, 10, 10).Equals(aabb2, .001));
}
[Test, ChildProcessTest]
public async Task AutoArrangeChildrenTests()
{
// arrange a single item around the origin
{
var scene = new InteractiveScene();
Object3D cube1;
scene.Children.Add(cube1 = new Object3D()
{
Mesh = PlatonicSolids.CreateCube(20, 20, 20),
Matrix = Matrix4X4.CreateTranslation(34, 22, 10)
});
Assert.IsTrue(new AxisAlignedBoundingBox(24, 12, 0, 44, 32, 20).Equals(cube1.GetAxisAlignedBoundingBox(), .001));
await scene.AutoArrangeChildren(Vector3.Zero);
Assert.IsTrue(new AxisAlignedBoundingBox(-10, -10, 0, 10, 10, 20).Equals(cube1.GetAxisAlignedBoundingBox(), .001));
}
// arrange a single item around a typical bed center
{
var scene = new InteractiveScene();
Object3D cube1;
scene.Children.Add(cube1 = new Object3D()
{
Mesh = PlatonicSolids.CreateCube(20, 20, 20),
Matrix = Matrix4X4.CreateTranslation(34, 22, 10)
});
Assert.IsTrue(new AxisAlignedBoundingBox(24, 12, 0, 44, 32, 20).Equals(cube1.GetAxisAlignedBoundingBox(), .001));
await scene.AutoArrangeChildren(new Vector3(100, 100, 0));
Assert.IsTrue(new AxisAlignedBoundingBox(90, 90, 0, 110, 110, 20).Equals(cube1.GetAxisAlignedBoundingBox(), .001));
}
// arrange 4 items
{
var scene = new InteractiveScene();
for (int i = 0; i < 4; i++)
{
scene.Children.Add(new Object3D()
{
Mesh = PlatonicSolids.CreateCube(20, 20, 20),
Matrix = Matrix4X4.CreateTranslation(i * 134, i * -122, 10)
});
}
var sceneAabb = scene.GetAxisAlignedBoundingBox();
Assert.Greater(sceneAabb.XSize, 160);
Assert.Greater(sceneAabb.YSize, 160);
await scene.AutoArrangeChildren(Vector3.Zero);
sceneAabb = scene.GetAxisAlignedBoundingBox();
Assert.Less(sceneAabb.XSize, 60);
Assert.Less(sceneAabb.YSize, 75);
}
// arrange 4 items, starting with 1 selected
{
var scene = new InteractiveScene();
Object3D child = null;
for (int i = 0; i < 4; i++)
{
scene.Children.Add(child = new Object3D()
{
Mesh = PlatonicSolids.CreateCube(20, 20, 20),
Matrix = Matrix4X4.CreateTranslation(i * 134, i * -122, 10)
});
}
scene.SelectedItem = child;
var sceneAabb = scene.GetAxisAlignedBoundingBox();
Assert.Greater(sceneAabb.XSize, 160);
Assert.Greater(sceneAabb.YSize, 160);
await scene.AutoArrangeChildren(Vector3.Zero);
sceneAabb = scene.GetAxisAlignedBoundingBox();
Assert.Less(sceneAabb.XSize, 60);
Assert.Less(sceneAabb.YSize, 75);
}
}
[Test, ChildProcessTest]
public void CreatesAndLinksAmfsForUnsavedMeshes()
{
AssetObject3D.AssetManager = new AssetManager();
var scene = new InteractiveScene();
scene.Children.Add(new Object3D
{
Mesh = PlatonicSolids.CreateCube(20, 20, 20)
});
string tempPath = GetSceneTempPath("links_amf");
string filePath = Path.Combine(tempPath, "some.mcx");
Object3D.AssetsPath = Path.Combine(tempPath, "Assets");
scene.Save(filePath);
Assert.IsTrue(File.Exists(filePath));
IObject3D loadedItem = Object3D.Load(filePath, CancellationToken.None);
Assert.IsTrue(loadedItem.Children.Count == 1);
IObject3D meshItem = loadedItem.Children.First();
Assert.IsTrue(!string.IsNullOrEmpty(meshItem.MeshPath));
Assert.IsTrue(File.Exists(Path.Combine(tempPath, "Assets", meshItem.MeshPath)));
Assert.IsNotNull(meshItem.Mesh);
Assert.IsTrue(meshItem.Mesh.Faces.Count > 0);
}
[Test, ChildProcessTest]
public async Task ResavedSceneRemainsConsistent()
{
AssetObject3D.AssetManager = new AssetManager();
var sceneContext = new BedConfig(null);
var scene = sceneContext.Scene;
scene.Children.Add(new Object3D
{
Mesh = PlatonicSolids.CreateCube(20, 20, 20)
});
string tempPath = GetSceneTempPath("resave_scene");
string filePath = Path.Combine(tempPath, "some.mcx");
// Set directory for asset resolution
Object3D.AssetsPath = Path.Combine(tempPath, "Assets");
if (Directory.Exists(Object3D.AssetsPath))
{
Directory.Delete(Object3D.AssetsPath, true);
}
Directory.CreateDirectory(Object3D.AssetsPath);
scene.Save(filePath);
Assert.AreEqual(1, Directory.GetFiles(tempPath).Length, "Only .mcx file should exists");
Assert.AreEqual(1, Directory.GetFiles(Path.Combine(tempPath, "Assets")).Length, "Only 1 asset should exist");
var originalFiles = Directory.GetFiles(tempPath).ToArray();
// Load the file from disk
IObject3D loadedItem = Object3D.Load(filePath, CancellationToken.None);
Assert.AreEqual(1, loadedItem.Children.Count);
// Ensure the UI scene is cleared
scene.Children.Modify(list => list.Clear());
// Reload the model
await Task.Run(() =>
{
sceneContext.Scene.Load(Object3D.Load(filePath, CancellationToken.None));
});
// Serialize and compare the two trees
string onDiskData = loadedItem.ToJson().Result;
string inMemoryData = scene.ToJson().Result;
//File.WriteAllText(@"c:\temp\file-a.txt", onDiskData);
//File.WriteAllText(@"c:\temp\file-b.txt", inMemoryData);
Assert.AreEqual(inMemoryData, onDiskData, "Serialized content should match");
Object3D.AssetsPath = Path.Combine(tempPath, "Assets");
// Save the scene a second time, validate that things remain the same
scene.Save(filePath);
onDiskData = loadedItem.ToJson().Result;
Assert.IsTrue(inMemoryData == onDiskData);
// Verify that no additional files get created on second save
Assert.AreEqual(1, Directory.GetFiles(tempPath).Length, "Only .mcx file should exists");
Assert.AreEqual(1, Directory.GetFiles(Path.Combine(tempPath, "Assets")).Length, "Only 1 asset should exist");
}
public InteractiveScene SampleScene()
{
Object3D group;
var scene = new InteractiveScene()
{
Color = Color.Black,
MaterialIndex = this.RootMaterialIndex,
OutputType = this.RootOutputType
};
var supergroup = new Object3D()
{
Name = "SuperGroup",
Color = Color.Violet,
MaterialIndex = this.SuperGroupMaterialIndex,
Matrix = this.SuperGroupMatrix,
OutputType = this.SuperGroupOutputType
};
scene.Children.Add(supergroup);
group = new Object3D()
{
Name = "GroupA",
Color = Color.Pink,
MaterialIndex = this.GroupMaterialIndex,
OutputType = this.GroupOutputType
};
supergroup.Children.Add(group);
group.Children.Add(new Object3D
{
Name = nameof(Color.Red),
Color = Color.Red,
MaterialIndex = this.RedMaterialIndex,
Matrix = this.RedMatrix,
OutputType = this.RedOutputType
});
group = new Object3D()
{
Name = "GroupB",
Color = Color.Pink,
MaterialIndex = this.GroupMaterialIndex,
OutputType = this.GroupOutputType
};
supergroup.Children.Add(group);
group.Children.Add(new Object3D
{
Name = nameof(Color.Green),
Color = Color.Green,
MaterialIndex = this.GreenMaterialIndex,
Matrix = this.GreenMatrix,
OutputType = this.GreenOutputType
});
group = new Object3D()
{
Name = "GroupB",
Color = Color.Pink,
MaterialIndex = this.GroupMaterialIndex,
OutputType = this.GroupOutputType
};
supergroup.Children.Add(group);
group.Children.Add(new Object3D
{
Name = nameof(Color.Blue),
Color = Color.Blue,
MaterialIndex = this.BlueMaterialIndex,
Matrix = this.BlueMatrix,
OutputType = this.BlueOutputType
});
return scene;
}
[Test, ChildProcessTest]
public void SaveSimpleScene()
{
var scene = new InteractiveScene();
scene.Children.Add(new Object3D());
string tempPath = GetSceneTempPath("simple_scene");
Object3D.AssetsPath = Path.Combine(tempPath, "Assets");
string filePath = Path.Combine(tempPath, "some.mcx");
scene.Save(filePath);
Assert.IsTrue(File.Exists(filePath));
IObject3D loadedItem = Object3D.Load(filePath, CancellationToken.None);
Assert.IsTrue(loadedItem.Children.Count == 1);
}
[SetUp]
public void SetupUserSettings()
{
StaticData.RootPath = MatterControlUtilities.StaticDataPath;
MatterControlUtilities.OverrideAppDataLocation(MatterControlUtilities.RootPath);
UserSettings.Instance.set(UserSettingsKey.PublicProfilesSha, "0"); //Clears DB so we will download the latest list
}
[Test, ChildProcessTest]
public void WorldColorBasicTest()
{
var scene = SampleScene();
var superGroup = scene.DescendantsAndSelf().Where(d => d.Name == "SuperGroup").FirstOrDefault();
var redItem = scene.DescendantsAndSelf().Where(d => d.Name == nameof(Color.Red)).FirstOrDefault();
var greenItem = scene.DescendantsAndSelf().Where(d => d.Name == nameof(Color.Green)).FirstOrDefault();
var blueItem = scene.DescendantsAndSelf().Where(d => d.Name == nameof(Color.Blue)).FirstOrDefault();
// Validate root
Assert.AreEqual(Color.Black, scene.Color, "Color property on root should be Black");
Assert.AreEqual(Color.Black, scene.WorldColor(), "WorldColor on root should be Black");
// Validate red node
Assert.AreEqual(Color.Red, redItem.Color, "Color property on node should be Red");
Assert.AreEqual(Color.Pink, redItem.WorldColor(redItem.Parent), "WorldColor on Red up to parent node should be Pink");
Assert.AreEqual(Color.Violet, redItem.WorldColor(superGroup), "WorldColor on Red up to supergroup should be Violet");
// Validate green node
Assert.AreEqual(Color.Green, greenItem.Color, "Color property on node should be Green");
Assert.AreEqual(Color.Pink, greenItem.WorldColor(greenItem.Parent), "WorldColor on Green up to parent node should be Pink");
Assert.AreEqual(Color.Violet, greenItem.WorldColor(superGroup), "WorldColor on Green up to supergroup should be Violet");
// Validate green node
Assert.AreEqual(Color.Blue, blueItem.Color, "Color property on node should be Green");
Assert.AreEqual(Color.Pink, blueItem.WorldColor(blueItem.Parent), "WorldColor on Blue up to parent node should be Pink");
Assert.AreEqual(Color.Violet, blueItem.WorldColor(superGroup), "WorldColor on Blue up to supergroup should be Violet");
// Validate WorldColor with null param
Assert.AreEqual(Color.Black, redItem.WorldColor(null), "WorldColor on Red with null param should be root color (Black)");
}
[Test, ChildProcessTest]
public void WorldFunctionNonExistingAncestorOverride()
{
var scene = SampleScene();
var redItem = scene.DescendantsAndSelf().Where(d => d.Name == nameof(Color.Red)).FirstOrDefault();
var nonAncestor = new Object3D();
// ************************************* WorldColor *************************************
// Validate root
Assert.AreEqual(Color.Black, scene.Color, "Color property on root should be Black");
Assert.AreEqual(Color.Black, scene.WorldColor(), "WorldColor on root should be Black");
// Validate red node
Assert.AreEqual(Color.Red, redItem.Color, "Color property on node should be Red");
// Validate WorldColor with non-ancestor param
Assert.AreEqual(Color.Black, redItem.WorldColor(nonAncestor), "WorldColor on Red with non-ancestor should be root color (Black)");
// ************************************* MaterialIndex *************************************
// Validate root
Assert.AreEqual(this.RootMaterialIndex, scene.MaterialIndex, "MaterialIndex property on root should be RootMaterialIndex");
Assert.AreEqual(this.RootMaterialIndex, scene.WorldMaterialIndex(), "WorldMaterialIndex on root should be RootMaterialIndex");
// Validate red node
Assert.AreEqual(this.RedMaterialIndex, redItem.MaterialIndex, "Color property on node should be Red");
// Validate WorldColor with non-ancestor param
Assert.AreEqual(this.RootMaterialIndex, redItem.WorldMaterialIndex(nonAncestor), "WorldMaterialIndex on Red with non-ancestor should be RootMaterialIndex");
// ************************************* WorldMaxtrix *************************************
// Validate root
Assert.AreEqual(this.RootMatrix, scene.Matrix, "Matrix property on root should be RootMatrix");
Assert.AreEqual(this.RootMatrix, scene.WorldMatrix(), "WorldMatrix on root should be RootMatrix");
// Validate red node
Assert.AreEqual(this.RedMatrix, redItem.Matrix, "Matrix property on node should be RedMatrix");
// Validate WorldColor with non-ancestor param
Assert.AreEqual(this.RedMatrix * this.GroupMatrix * this.SuperGroupMatrix, redItem.WorldMatrix(nonAncestor), "WorldMatrix on Red with non-ancestor should be RootMaterialIndex");
// ************************************* WorldOutputType *************************************
// Validate root
Assert.AreEqual(this.RootOutputType, scene.OutputType, "OutputType property on root should be RootOutputType");
Assert.AreEqual(this.RootOutputType, scene.WorldOutputType(), "WorldOutputType on root should be RootOutputType");
// Validate red node
Assert.AreEqual(this.RedOutputType, redItem.OutputType, "Color property on node should be Red");
// Validate WorldColor with non-ancestor param
Assert.AreEqual(this.RootOutputType, redItem.WorldOutputType(nonAncestor), "WorldOutputType on Red with non-ancestor should be RootOutputType");
}
[Test, ChildProcessTest]
public void WorldMaterialIndexBasicTest()
{
var scene = SampleScene();
var superGroup = scene.DescendantsAndSelf().Where(d => d.Name == "SuperGroup").FirstOrDefault();
var redItem = scene.DescendantsAndSelf().Where(d => d.Name == nameof(Color.Red)).FirstOrDefault();
var greenItem = scene.DescendantsAndSelf().Where(d => d.Name == nameof(Color.Green)).FirstOrDefault();
var blueItem = scene.DescendantsAndSelf().Where(d => d.Name == nameof(Color.Blue)).FirstOrDefault();
// Validate root
Assert.AreEqual(this.RootMaterialIndex, scene.MaterialIndex, "MaterialIndex property on root should be RootMaterialIndex");
Assert.AreEqual(this.RootMaterialIndex, scene.WorldMaterialIndex(), "WorldMaterialIndex on root should be RootMaterialIndex");
// Validate red node
Assert.AreEqual(this.RedMaterialIndex, redItem.MaterialIndex, "MaterialIndex property on node should be RedMaterialIndex");
Assert.AreEqual(this.GroupMaterialIndex, redItem.WorldMaterialIndex(redItem.Parent), "WorldMaterialIndex on Red up to parent node should be GroupMaterialIndex");
Assert.AreEqual(this.SuperGroupMaterialIndex, redItem.WorldMaterialIndex(superGroup), "WorldMaterialIndex on Red up to supergroup should be SuperGroupMaterialIndex");
// Validate green node
Assert.AreEqual(this.GreenMaterialIndex, greenItem.MaterialIndex, "MaterialIndex property on node should be GreenMaterialIndex");
Assert.AreEqual(this.GroupMaterialIndex, greenItem.WorldMaterialIndex(greenItem.Parent), "WorldMaterialIndex on Green up to parent node should be GroupMaterialIndex");
Assert.AreEqual(this.SuperGroupMaterialIndex, greenItem.WorldMaterialIndex(superGroup), "WorldMaterialIndex on Green up to supergroup should be SuperGroupMaterialIndex");
// Validate green node
Assert.AreEqual(this.BlueMaterialIndex, blueItem.MaterialIndex, "MaterialIndex property on node should be BlueMaterialIndex");
Assert.AreEqual(this.GroupMaterialIndex, blueItem.WorldMaterialIndex(blueItem.Parent), "WorldMaterialIndex on Blue up to parent node should be GroupMaterialIndex");
Assert.AreEqual(this.SuperGroupMaterialIndex, blueItem.WorldMaterialIndex(superGroup), "WorldMaterialIndex on Blue up to supergroup should be SuperGroupMaterialIndex");
// Validate MaterialIndex with null param
Assert.AreEqual(this.RootMaterialIndex, redItem.WorldMaterialIndex(null), "WorldMaterialIndex on Red with null param should be root color (RootMaterialIndex)");
}
[Test, ChildProcessTest]
public void WorldMatrixBasicTest()
{
var scene = SampleScene();
var superGroup = scene.DescendantsAndSelf().Where(d => d.Name == "SuperGroup").FirstOrDefault();
var redItem = scene.DescendantsAndSelf().Where(d => d.Name == nameof(Color.Red)).FirstOrDefault();
var greenItem = scene.DescendantsAndSelf().Where(d => d.Name == nameof(Color.Green)).FirstOrDefault();
var blueItem = scene.DescendantsAndSelf().Where(d => d.Name == nameof(Color.Blue)).FirstOrDefault();
// Validate root
Assert.AreEqual(this.RootMatrix, scene.Matrix, "Matrix property on root should be RootMatrix");
Assert.AreEqual(this.RootMatrix, scene.WorldMatrix(), "WorldMatrix on root should be RootMatrix");
// Validate red node
Assert.AreEqual(this.RedMatrix, redItem.Matrix, "Matrix property on node should be RedMatrix");
Assert.AreEqual(redItem.Matrix * this.GroupMatrix, redItem.WorldMatrix(redItem.Parent), "WorldMatrix on Red up to parent node should be GroupMatrix");
Assert.AreEqual(this.RedMatrix * this.GroupMatrix * this.SuperGroupMatrix, redItem.WorldMatrix(superGroup), "WorldMatrix on Red up to supergroup invalid");
// Validate green node
Assert.AreEqual(this.GreenMatrix, greenItem.Matrix, "Matrix property on node should be GreenMatrix");
Assert.AreEqual(this.GreenMatrix * this.GroupMatrix, greenItem.WorldMatrix(greenItem.Parent), "WorldMatrix on Green up to parent node should be GroupMatrix");
Assert.AreEqual(this.GreenMatrix * this.GroupMatrix * this.SuperGroupMatrix, greenItem.WorldMatrix(superGroup), "WorldMatrix on Green up to supergroup should be SuperGroupMatrix");
// Validate green node
Assert.AreEqual(this.BlueMatrix, blueItem.Matrix, "Matrix property on node should be BlueMatrix");
Assert.AreEqual(this.BlueMatrix * this.GroupMatrix, blueItem.WorldMatrix(blueItem.Parent), "WorldMatrix on Blue up to parent node should be GroupMatrix");
Assert.AreEqual(this.BlueMatrix * this.GroupMatrix * this.SuperGroupMatrix, blueItem.WorldMatrix(superGroup), "WorldMatrix on Blue up to supergroup should be SuperGroupMatrix");
// Validate Matrix with null param
Assert.AreEqual(this.RedMatrix * this.GroupMatrix * this.SuperGroupMatrix, redItem.WorldMatrix(null), "WorldMatrix on Red with null param should be root color (RootMatrix)");
}
[Test, ChildProcessTest]
public void WorldOutputTypeBasicTest()
{
var scene = SampleScene();
var superGroup = scene.DescendantsAndSelf().Where(d => d.Name == "SuperGroup").FirstOrDefault();
var redItem = scene.DescendantsAndSelf().Where(d => d.Name == nameof(Color.Red)).FirstOrDefault();
var greenItem = scene.DescendantsAndSelf().Where(d => d.Name == nameof(Color.Green)).FirstOrDefault();
var blueItem = scene.DescendantsAndSelf().Where(d => d.Name == nameof(Color.Blue)).FirstOrDefault();
// Validate root
Assert.AreEqual(this.RootOutputType, scene.OutputType, "OutputType property on root should be RootOutputType");
Assert.AreEqual(this.RootOutputType, scene.WorldOutputType(), "WorldOutputType on root should be RootOutputType");
// Validate red node
Assert.AreEqual(this.RedOutputType, redItem.OutputType, "OutputType property on node should be RedOutputType");
Assert.AreEqual(this.GroupOutputType, redItem.WorldOutputType(redItem.Parent), "WorldOutputType on Red up to parent node should be GroupOutputType");
Assert.AreEqual(this.SuperGroupOutputType, redItem.WorldOutputType(superGroup), "WorldOutputType on Red up to supergroup should be SuperGroupOutputType");
// Validate green node
Assert.AreEqual(this.GreenOutputType, greenItem.OutputType, "OutputType property on node should be GreenOutputType");
Assert.AreEqual(this.GroupOutputType, greenItem.WorldOutputType(greenItem.Parent), "WorldOutputType on Green up to parent node should be GroupOutputType");
Assert.AreEqual(this.SuperGroupOutputType, greenItem.WorldOutputType(superGroup), "WorldOutputType on Green up to supergroup should be SuperGroupOutputType");
// Validate green node
Assert.AreEqual(this.BlueOutputType, blueItem.OutputType, "OutputType property on node should be BlueOutputType");
Assert.AreEqual(this.GroupOutputType, blueItem.WorldOutputType(blueItem.Parent), "WorldOutputType on Blue up to parent node should be GroupOutputType");
Assert.AreEqual(this.SuperGroupOutputType, blueItem.WorldOutputType(superGroup), "WorldOutputType on Blue up to supergroup should be SuperGroupOutputType");
// Validate OutputType with null param
Assert.AreEqual(this.RootOutputType, redItem.WorldOutputType(null), "WorldOutputType on Red with null param should be root color (RootOutputType)");
}
}
}

View file

@ -0,0 +1,17 @@
<NUnitProject>
<Settings activeconfig="Debug" processModel="Multiple" domainUsage="Multiple" />
<Config name="Debug" binpathtype="Auto" runtimeFramework="v4.0.21006">
<assembly path="..\Submodules\agg-sharp\Tests\Agg.Tests\bin\Debug\Agg.Tests.dll" />
<assembly path="MatterControl.Tests\bin\Debug\MatterControl.Tests.dll" />
<assembly path="..\Submodules\MatterSlice\Tests\MatterSlice.Tests\bin\Debug\MatterSlice.Tests.dll" />
<assembly path="MatterControl.AutomationTests\bin\Debug\MatterControl.AutomationTests.dll" />
<assembly path="..\..\Plugins\CloudServices\CloudServices.Tests\bin\Debug\CloudServices.Tests.dll" />
</Config>
<Config name="Release" binpathtype="Auto" runtimeFramework="v4.0.21006">
<assembly path="MatterControl.Tests\bin\Release\MatterControl.Tests.dll" />
<assembly path="..\Submodules\agg-sharp\Tests\Agg.Tests\bin\Release\Agg.Tests.dll" />
<assembly path="..\Submodules\MatterSlice\Tests\MatterSlice.Tests\bin\Release\MatterSlice.Tests.dll" />
<assembly path="MatterControl.AutomationTests\bin\Release\MatterControl.AutomationTests.dll" />
<assembly path="..\..\Plugins\CloudServices\CloudServices.Tests\bin\Release\CloudServices.Tests.dll" />
</Config>
</NUnitProject>

View file

@ -0,0 +1,11 @@
<NUnitProject>
<Settings activeconfig="Debug" processModel="Multiple" domainUsage="Multiple" />
<Config name="Debug" binpathtype="Auto" runtimeFramework="v4.0.21006">
<assembly path="MatterControl.AutomationTests\bin\Debug\MatterControl.AutomationTests.dll" />
<assembly path="..\..\CloudServicesPlugin\CloudServices.Tests\bin\Debug\CloudServices.Tests.dll" />
</Config>
<Config name="Release" binpathtype="Auto" runtimeFramework="v4.0.21006">
<assembly path="MatterControl.AutomationTests\bin\Release\MatterControl.AutomationTests.dll" />
<assembly path="..\..\CloudServicesPlugin\CloudServices.Tests\bin\Release\CloudServices.Tests.dll" />
</Config>
</NUnitProject>

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,5 @@
{
"ProjectFiles": [],
"ProjectName": "Test Project",
"ProjectDateCreated": "2015-09-03T09:36:12"
}

View file

@ -0,0 +1,16 @@
{
"ProjectFiles": [
{
"DateAdded": "2015-09-01T10:50:29.5703883-07:00",
"FileLocation": "C:\\Development\\MatterControl\\Tests\\TestData\\TestMeshes\\LibraryProviderData\\Box20x20x10.stl",
"Name": "Box20x20x10",
"PrintCount": 0,
"PrintItemCollectionID": 0,
"ReadOnly": false,
"Protected": false,
"Id": 0
}
],
"ProjectName": "Test Project",
"ProjectDateCreated": "2015-09-01T10:50:44"
}

View file

@ -0,0 +1,36 @@
{
"ProjectFiles": [
{
"DateAdded": "2015-09-03T09:37:47.8486405-07:00",
"FileLocation": "[QueueItems]\\2013-01-25_Mouthpiece_v2.stl",
"Name": "2013-01-25_Mouthpiece_v2",
"PrintCount": 0,
"PrintItemCollectionID": 0,
"ReadOnly": false,
"Protected": false,
"Id": 0
},
{
"DateAdded": "2015-09-03T09:37:47.9036436-07:00",
"FileLocation": "[QueueItems]\\Batman.stl",
"Name": "Batman",
"PrintCount": 0,
"PrintItemCollectionID": 0,
"ReadOnly": false,
"Protected": false,
"Id": 0
},
{
"DateAdded": "2015-09-03T09:37:55.7320914-07:00",
"FileLocation": "[QueueItems]\\Fennec_Fox.stl",
"Name": "Fennec_Fox",
"PrintCount": 0,
"PrintItemCollectionID": 0,
"ReadOnly": false,
"Protected": false,
"Id": 0
}
],
"ProjectName": "Test Project",
"ProjectDateCreated": "2015-09-03T09:37:55"
}

Some files were not shown because too many files have changed in this diff Show more