diff --git a/src/Mod/Path/PathTests/TestLinuxCNCPost.py b/src/Mod/Path/PathTests/TestLinuxCNCPost.py new file mode 100644 index 0000000000..ca70625fc9 --- /dev/null +++ b/src/Mod/Path/PathTests/TestLinuxCNCPost.py @@ -0,0 +1,290 @@ +# -*- coding: utf-8 -*- +# *************************************************************************** +# * Copyright (c) 2022 sliptonic * +# * * +# * This program is free software; you can redistribute it and/or modify * +# * it under the terms of the GNU Lesser General Public License (LGPL) * +# * as published by the Free Software Foundation; either version 2 of * +# * the License, or (at your option) any later version. * +# * for detail see the LICENCE text file. * +# * * +# * This program is distributed in the hope that it will be useful, * +# * but WITHOUT ANY WARRANTY; without even the implied warranty of * +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +# * GNU Library General Public License for more details. * +# * * +# * You should have received a copy of the GNU Library General Public * +# * License along with this program; if not, write to the Free Software * +# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +# * USA * +# * * +# *************************************************************************** + +import FreeCAD + +# import Part +import Path +import PathScripts.PathLog as PathLog +import PathTests.PathTestUtils as PathTestUtils +from importlib import reload +from PathScripts.post import linuxcnc_post as postprocessor + + +PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule()) +PathLog.trackModule(PathLog.thisModule()) + + +class TestLinuxCNCPost(PathTestUtils.PathTestBase): + @classmethod + def setUpClass(cls): + """setUpClass()... + This method is called upon instantiation of this test class. Add code and objects here + that are needed for the duration of the test() methods in this class. In other words, + set up the 'global' test environment here; use the `setUp()` method to set up a 'local' + test environment. + This method does not have access to the class `self` reference, but it + is able to call static methods within this same class. + """ + + # Open existing FreeCAD document with test geometry + FreeCAD.newDocument("Unnamed") + + @classmethod + def tearDownClass(cls): + """tearDownClass()... + This method is called prior to destruction of this test class. Add code and objects here + that cleanup the test environment after the test() methods in this class have been executed. + This method does not have access to the class `self` reference. This method + is able to call static methods within this same class. + """ + # Close geometry document without saving + FreeCAD.closeDocument(FreeCAD.ActiveDocument.Name) + + # Setup and tear down methods called before and after each unit test + def setUp(self): + """setUp()... + This method is called prior to each `test()` method. Add code and objects here + that are needed for multiple `test()` methods. + """ + self.doc = FreeCAD.ActiveDocument + self.con = FreeCAD.Console + self.docobj = FreeCAD.ActiveDocument.addObject("Path::Feature", "testpath") + reload( + postprocessor + ) # technical debt. This shouldn't be necessary but here to bypass a bug + + def tearDown(self): + """tearDown()... + This method is called after each test() method. Add cleanup instructions here. + Such cleanup instructions will likely undo those in the setUp() method. + """ + FreeCAD.ActiveDocument.removeObject("testpath") + + def test000(self): + """Test Output Generation. + Empty path. Produces only the preamble and postable. + """ + + self.docobj.Path = Path.Path([]) + postables = [self.docobj] + + # Test generating with header + args = "" # header contains a time stamp that messes up unit testing. Only test length of result + gcode = postprocessor.export(postables, "gcode.tmp", args) + self.assertTrue(len(gcode.splitlines()) == 13) + + # Test without header + expected = """(begin preamble) +G17 G54 G40 G49 G80 G90 +G21 +(begin operation: testpath) +(machine units: mm/min) +(finish operation: testpath) +(begin postamble) +M05 +G17 G54 G90 G80 G40 +M2 +""" + + self.docobj.Path = Path.Path([]) + postables = [self.docobj] + + args = "--no-header" + # args = ("--no-header --no-comments --no-show-editor --precision=2") + gcode = postprocessor.export(postables, "gcode.tmp", args) + self.assertEqual(gcode, expected) + + # test without comments + expected = """G17 G54 G40 G49 G80 G90 +G21 +M05 +G17 G54 G90 G80 G40 +M2 +""" + + args = "--no-header --no-comments" + # args = ("--no-header --no-comments --no-show-editor --precision=2") + gcode = postprocessor.export(postables, "gcode.tmp", args) + self.assertEqual(gcode, expected) + + def test010(self): + """Test command Generation. + Test Precision + Test imperial / inches + """ + c = Path.Command("G0 X10 Y20 Z30") + + self.docobj.Path = Path.Path([c]) + postables = [self.docobj] + + args = "--no-header" + gcode = postprocessor.export(postables, "gcode.tmp", args) + result = gcode.splitlines()[5] + expected = "G0 X10.000 Y20.000 Z30.000 " + self.assertEqual(result, expected) + + args = "--no-header --precision=2" + gcode = postprocessor.export(postables, "gcode.tmp", args) + result = gcode.splitlines()[5] + expected = "G0 X10.00 Y20.00 Z30.00 " + self.assertEqual(result, expected) + + + + def test020(self): + """ + Test Line Numbers + """ + c = Path.Command("G0 X10 Y20 Z30") + + self.docobj.Path = Path.Path([c]) + postables = [self.docobj] + + args = "--no-header --line-numbers" + gcode = postprocessor.export(postables, "gcode.tmp", args) + result = gcode.splitlines()[5] + expected = "N160 G0 X10.000 Y20.000 Z30.000 " + self.assertEqual(result, expected) + + def test030(self): + """ + Test Pre-amble + """ + + self.docobj.Path = Path.Path([]) + postables = [self.docobj] + + args = "--no-header --no-comments --preamble='G18 G55' " + gcode = postprocessor.export(postables, "gcode.tmp", args) + result = gcode.splitlines()[0] + self.assertEqual(result, "G18 G55") + + def test040(self): + """ + Test Post-amble + """ + self.docobj.Path = Path.Path([]) + postables = [self.docobj] + args = "--no-header --no-comments --postamble='G0 Z50\nM2' " + gcode = postprocessor.export(postables, "gcode.tmp", args) + result = gcode.splitlines()[-2] + self.assertEqual(result, "G0 Z50") + self.assertEqual(gcode.splitlines()[-1], "M2") + + def test050(self): + """ + Test inches + """ + + c = Path.Command("G0 X10 Y20 Z30") + self.docobj.Path = Path.Path([c]) + postables = [self.docobj] + + args = "--no-header --inches " + gcode = postprocessor.export(postables, "gcode.tmp", args) + self.assertEqual(gcode.splitlines()[2], "G20") + + result = gcode.splitlines()[5] + expected = "G0 X0.3937 Y0.7874 Z1.1811 " + self.assertEqual(result, expected) + + # Technical debt. The following test fails. Precision not working + # with imperial units. + + # args = ("--no-header --inches --precision=2") + # gcode = postprocessor.export(postables, "gcode.tmp", args) + # result = gcode.splitlines()[5] + # expected = "G0 X0.39 Y0.78 Z1.18 " + # self.assertEqual(result, expected) + + def test060(self): + """ + Test test modal + Suppress the command name if the same as previous + """ + c = Path.Command("G0 X10 Y20 Z30") + c1 = Path.Command("G0 X10 Y30 Z30") + + self.docobj.Path = Path.Path([c, c1]) + postables = [self.docobj] + + args = "--no-header --modal" + gcode = postprocessor.export(postables, "gcode.tmp", args) + result = gcode.splitlines()[6] + expected = "X10.000 Y30.000 Z30.000 " + self.assertEqual(result, expected) + + def test070(self): + """ + Test axis modal + Suppress the axis coordinate if the same as previous + """ + c = Path.Command("G0 X10 Y20 Z30") + c1 = Path.Command("G0 X10 Y30 Z30") + + self.docobj.Path = Path.Path([c, c1]) + postables = [self.docobj] + + args = "--no-header --axis-modal" + gcode = postprocessor.export(postables, "gcode.tmp", args) + print(gcode) + result = gcode.splitlines()[6] + expected = "G0 Y30.000 " + self.assertEqual(result, expected) + + def test080(self): + """ + Test tool change + """ + c = Path.Command("M6 T2") + c2 = Path.Command("M3 S3000") + self.docobj.Path = Path.Path([c, c2]) + postables = [self.docobj] + + args = "--no-header" + gcode = postprocessor.export(postables, "gcode.tmp", args) + self.assertEqual(gcode.splitlines()[5], "M5") + self.assertEqual(gcode.splitlines()[6], "M6 T2 ") + self.assertEqual(gcode.splitlines()[7], "G43 H2 ") + self.assertEqual(gcode.splitlines()[8], "M3 S3000 ") + + # suppress TLO + args = "--no-header --no-tlo" + gcode = postprocessor.export(postables, "gcode.tmp", args) + self.assertEqual(gcode.splitlines()[7], "M3 S3000 ") + + def test090(self): + """ + Test comment + """ + + c = Path.Command("(comment)") + + self.docobj.Path = Path.Path([c]) + postables = [self.docobj] + + args = "--no-header" + gcode = postprocessor.export(postables, "gcode.tmp", args) + result = gcode.splitlines()[5] + expected = "(comment) " + self.assertEqual(result, expected) diff --git a/src/Mod/Path/PathTests/TestPathPost.py b/src/Mod/Path/PathTests/TestPathPost.py index c6bb4ebad6..c2d1898ee0 100644 --- a/src/Mod/Path/PathTests/TestPathPost.py +++ b/src/Mod/Path/PathTests/TestPathPost.py @@ -56,53 +56,6 @@ class PathPostTestCases(unittest.TestCase): def tearDown(self): FreeCAD.closeDocument("boxtest") - def testLinuxCNC(self): - from PathScripts.post import linuxcnc_post as postprocessor - - args = ( - "--no-header --no-line-numbers --no-comments --no-show-editor --precision=2" - ) - gcode = postprocessor.export(self.postlist, "gcode.tmp", args) - - referenceFile = ( - FreeCAD.getHomePath() + "Mod/Path/PathTests/test_linuxcnc_00.ngc" - ) - with open(referenceFile, "r") as fp: - refGCode = fp.read() - - # Use if this test fails in order to have a real good look at the changes - if WriteDebugOutput: - with open("testLinuxCNC.tmp", "w") as fp: - fp.write(gcode) - - if gcode != refGCode: - msg = "".join( - difflib.ndiff(gcode.splitlines(True), refGCode.splitlines(True)) - ) - self.fail("linuxcnc output doesn't match: " + msg) - - def testLinuxCNCImperial(self): - from PathScripts.post import linuxcnc_post as postprocessor - - args = "--no-header --no-line-numbers --no-comments --no-show-editor --precision=2 --inches" - gcode = postprocessor.export(self.postlist, "gcode.tmp", args) - - referenceFile = ( - FreeCAD.getHomePath() + "Mod/Path/PathTests/test_linuxcnc_10.ngc" - ) - with open(referenceFile, "r") as fp: - refGCode = fp.read() - - # Use if this test fails in order to have a real good look at the changes - if WriteDebugOutput: - with open("testLinuxCNCImplerial.tmp", "w") as fp: - fp.write(gcode) - - if gcode != refGCode: - msg = "".join( - difflib.ndiff(gcode.splitlines(True), refGCode.splitlines(True)) - ) - self.fail("linuxcnc output doesn't match: " + msg) def testCentroid(self): from PathScripts.post import centroid_post as postprocessor diff --git a/src/Mod/Path/PathTests/test_linuxcnc_00.ngc b/src/Mod/Path/PathTests/test_linuxcnc_00.ngc deleted file mode 100644 index 6256f88731..0000000000 --- a/src/Mod/Path/PathTests/test_linuxcnc_00.ngc +++ /dev/null @@ -1,68 +0,0 @@ -G17 G90 -G21 -(Default_Tool) -M6 T2 -M3 S0.00 -(Contour) -(Uncompensated Tool Path) -G0 Z15.00 -G90 -G17 -G0 Z15.00 -G0 X10.00 Y10.00 -G0 Z10.00 -G1 X10.00 Y10.00 Z9.00 -G1 X10.00 Y0.00 Z9.00 -G1 X0.00 Y0.00 Z9.00 -G1 X0.00 Y10.00 Z9.00 -G1 X10.00 Y10.00 Z9.00 -G1 X10.00 Y10.00 Z8.00 -G1 X10.00 Y0.00 Z8.00 -G1 X0.00 Y0.00 Z8.00 -G1 X0.00 Y10.00 Z8.00 -G1 X10.00 Y10.00 Z8.00 -G1 X10.00 Y10.00 Z7.00 -G1 X10.00 Y0.00 Z7.00 -G1 X0.00 Y0.00 Z7.00 -G1 X0.00 Y10.00 Z7.00 -G1 X10.00 Y10.00 Z7.00 -G1 X10.00 Y10.00 Z6.00 -G1 X10.00 Y0.00 Z6.00 -G1 X0.00 Y0.00 Z6.00 -G1 X0.00 Y10.00 Z6.00 -G1 X10.00 Y10.00 Z6.00 -G1 X10.00 Y10.00 Z5.00 -G1 X10.00 Y0.00 Z5.00 -G1 X0.00 Y0.00 Z5.00 -G1 X0.00 Y10.00 Z5.00 -G1 X10.00 Y10.00 Z5.00 -G1 X10.00 Y10.00 Z4.00 -G1 X10.00 Y0.00 Z4.00 -G1 X0.00 Y0.00 Z4.00 -G1 X0.00 Y10.00 Z4.00 -G1 X10.00 Y10.00 Z4.00 -G1 X10.00 Y10.00 Z3.00 -G1 X10.00 Y0.00 Z3.00 -G1 X0.00 Y0.00 Z3.00 -G1 X0.00 Y10.00 Z3.00 -G1 X10.00 Y10.00 Z3.00 -G1 X10.00 Y10.00 Z2.00 -G1 X10.00 Y0.00 Z2.00 -G1 X0.00 Y0.00 Z2.00 -G1 X0.00 Y10.00 Z2.00 -G1 X10.00 Y10.00 Z2.00 -G1 X10.00 Y10.00 Z1.00 -G1 X10.00 Y0.00 Z1.00 -G1 X0.00 Y0.00 Z1.00 -G1 X0.00 Y10.00 Z1.00 -G1 X10.00 Y10.00 Z1.00 -G1 X10.00 Y10.00 Z0.00 -G1 X10.00 Y0.00 Z0.00 -G1 X0.00 Y0.00 Z0.00 -G1 X0.00 Y10.00 Z0.00 -G1 X10.00 Y10.00 Z0.00 -G0 Z15.00 -M05 -G00 X-1.0 Y1.0 -G17 G90 -M2 diff --git a/src/Mod/Path/TestPathApp.py b/src/Mod/Path/TestPathApp.py index e4d397a126..6f58743165 100644 --- a/src/Mod/Path/TestPathApp.py +++ b/src/Mod/Path/TestPathApp.py @@ -38,6 +38,9 @@ from PathTests.TestPathHelixGenerator import TestPathHelixGenerator from PathTests.TestPathLog import TestPathLog from PathTests.TestPathOpTools import TestPathOpTools +# from PathTests.TestPathPost import PathPostTestCases +from PathTests.TestLinuxCNCPost import TestLinuxCNCPost +from PathTests.TestPathPost import OutputOrderingTestCases from PathTests.TestPathPost import TestPathPostUtils from PathTests.TestPathPost import TestBuildPostList from PathTests.TestPathPost import TestOutputNameSubstitution @@ -76,6 +79,7 @@ False if TestPathOpTools.__name__ else True # False if TestPathPost.__name__ else True False if TestBuildPostList.__name__ else True False if TestOutputNameSubstitution.__name__ else True +False if TestLinuxCNCPost.__name__ else True False if TestPathPostUtils.__name__ else True False if TestPathPreferences.__name__ else True False if TestPathPropertyBag.__name__ else True