def test_parse_multiple_lines_mid(self):
        """Test parse to parse a line in the middle of multiple lines."""
        unit = GcodeParser()
        source = (
            "G28 ;Home\n" +
            "G1 X1 Y2\n" +
            "G20"
        )
        unit.parse(source)  # Read the first line

        result = unit.parse()

        self.assertEqual(result, unit, "The instance should be returned")
        self.assertParserProperties(
            unit=unit,
            source=source,
            offset=10,
            length=9,
            text="G1 X1 Y2",
            type="G",
            code=1,
            parameters="X1 Y2",
            parameterDict=OrderedDict([
                ("X", 1),
                ("Y", 2)
            ]),
            eol="\n"
        )
    def test_gcode_setter_noSubCode(self):
        """Test the gcode setter when no subCode is supplied."""
        unit = GcodeParser()

        unit.gcode = "G0"

        self.assertEqual(unit.type, "G", "The type should be 'G'.")
        self.assertEqual(unit.code, 0, "The code should be 0.")
        self.assertIsNone(unit.subCode, "The subCode should be None.")
        self.assertEqual(unit.gcode, "G0", "The gcode should be 'G0'.")
        self.assertEqual(unit.commandString, "G0", "The commandString should be 'G0'.")

        unit.gcode = "M17"

        self.assertEqual(unit.type, "M", "The type should be 'M'.")
        self.assertEqual(unit.code, 17, "The code should be 17.")
        self.assertIsNone(unit.subCode, "The subCode should be None.")
        self.assertEqual(unit.gcode, "M17", "The gcode should be 'M17'.")
        self.assertEqual(unit.commandString, "M17", "The commandString should be 'M17'.")

        unit.gcode = "T2"

        self.assertEqual(unit.type, "T", "The type should be 'T'.")
        self.assertEqual(unit.code, 2, "The code should be 2.")
        self.assertEqual(unit.gcode, "T2", "The gcode should be 'T2'.")
        self.assertIsNone(unit.subCode, "The subCode should be None.")
        self.assertEqual(unit.commandString, "T2", "The commandString should be 'T2'.")
    def test_gcode_setter_subCode(self):
        """Test the gcode setter when supplied with a subcode."""
        unit = GcodeParser()

        unit.gcode = "G1.2"

        self.assertEqual(unit.type, "G", "The type should be 'G'.")
        self.assertEqual(unit.code, 1, "The code should be 1.")
        self.assertEqual(unit.subCode, 2, "The subCode should be 2.")
        self.assertEqual(unit.gcode, "G1", "The gcode should be 'G1'.")
        self.assertEqual(unit.commandString, "G1.2", "The commandString should be 'G1.2'.")

        unit.gcode = "M17.1"

        self.assertEqual(unit.type, "M", "The type should be 'M'.")
        self.assertEqual(unit.code, 17, "The code should be 17.")
        self.assertEqual(unit.subCode, 1, "The subCode should be 1.")
        self.assertEqual(unit.gcode, "M17", "The gcode should be 'M17'.")
        self.assertEqual(unit.commandString, "M17.1", "The commandString should be 'M17.1'.")

        # T codes don't support subCodes
        with self.assertRaises(ValueError):
            unit.gcode = "T2.3"

        # No side-effects from exception
        self.assertEqual(unit.type, "M", "The type should be 'M'.")
        self.assertEqual(unit.code, 17, "The code should be 17.")
        self.assertEqual(unit.subCode, 1, "The subCode should be 1.")
    def test_buildCommand_no_kwargs(self):
        """Test buildCommand when passed a GCode command and no kwargs."""
        unit = GcodeParser()

        result = unit.buildCommand("G0")

        self.assertEqual(result, "G0", "The returned command should be 'G0'")
    def test_buildCommand_argValueEmptyString(self):
        """Test buildCommand when passed a GCode command and a kwarg set to empty string."""
        unit = GcodeParser()

        result = unit.buildCommand("G0", X="")

        self.assertEqual(result, "G0 X", "The returned command should be 'G0 X'")
    def test_buildCommand_argValueNone(self):
        """Test buildCommand when passed a GCode command and a kwarg set to None."""
        unit = GcodeParser()

        result = unit.buildCommand("G0", X=None)

        self.assertEqual(result, "G0 X", "The returned command should be 'G0 X'")
    def test_parameters_setter_valid_escapes(self):
        """Test the parameters setter when valid parameters with escaped chars are provided."""
        unit = GcodeParser()
        unit.parse("G1")
        self.assertEqual(unit.commandString, "G1", "The commandString should be 'G1'.")

        unit.parameters = r"X10 \\ \;Not a comment"

        self.assertEqual(
            unit.parameters, r"X10 \\ \;Not a comment",
            "The parameters should be the expected value."
        )
        self.assertEqual(
            unit.commandString, r"G1 X10 \\ \;Not a comment",
            "The commandString should be the expected value."
        )
        self.assertEqual(
            unit.parameterDict,
            OrderedDict([
                ("X", 10),
                ("N", None),
                ("O", None),
                ("T", None),
                ("A", None),
                ("C", None),
                ("M", None),
                ("E", None),
                ("", r"\\ \;Not a comment")
            ]),
            "The parameterDict should be the expected value."
        )
    def test_parameters_setter_valid_whitespace(self):
        """Test parameters setter when valid parameters are provided with extraneous whitespace."""
        unit = GcodeParser()
        unit.parse("G1")
        self.assertEqual(unit.commandString, "G1", "The commandString should be 'G1'.")

        unit.parameters = "   X  10 Y3.2 Foo  "

        self.assertEqual(
            unit.parameters, "X  10 Y3.2 Foo",
            "The parameters should be the expected value."
        )
        self.assertEqual(
            unit.commandString, "G1 X  10 Y3.2 Foo",
            "The commandString should be the expected value."
        )
        self.assertEqual(
            unit.parameterDict,
            OrderedDict([
                ("X", 10),
                ("Y", 3.2),
                ("F", None),
                ("O", None),
                ("", "Foo")
            ]),
            "The parameterDict should be the expected value."
        )
    def test_buildCommand_one_kwarg(self):
        """Test buildCommand when passed a GCode command and one kwargs."""
        unit = GcodeParser()

        result = unit.buildCommand("G0", X=10)

        self.assertEqual(result, "G0 X10", "The returned command should be 'G0 X10'")
    def test_parameters_setter_invalid_comment(self):
        """Test parameters setter when parameters provided include a comment-like value."""
        unit = GcodeParser()
        unit.parse("G1")
        self.assertEqual(unit.commandString, "G1", "The commandString should be 'G1'.")

        with self.assertRaises(ValueError):
            unit.parameters = "X10 Y3.2 Foo ;Invalid"
    def test_stringify_leadingWhitespaceFalse(self):
        """Test stringify when the includeLeadingWhitespace parameter is False."""
        unit = GcodeParser()
        unit.parse("   G28")

        result = unit.stringify(includeLeadingWhitespace=False)

        self.assertEqual(result, "G28", "The result should have the leading whitespace removed")
    def test_stringify_notGcodeText(self):
        """Test stringify when the text is not a valid Gcode line."""
        unit = GcodeParser()
        unit.parse("not gcode")

        result = unit.stringify()

        self.assertEqual(result, "not gcode", "The result should be the original text")
    def test_stringify_leadingWhitespaceTrue(self):
        """Test stringify when the includeLeadingWhitespace parameter is True."""
        unit = GcodeParser()
        unit.parse("   G28")

        result = unit.stringify(includeLeadingWhitespace=True)

        self.assertEqual(result, "   G28", "The result should be the original text")
    def test_parameterItems_empty_string(self):
        """Test parameterItems when provided an empty string."""
        unit = GcodeParser()

        self.assertIsNone(unit.parameters, "The parameters should be None.")

        result = [item for item in unit.parameterItems("")]

        self.assertEqual(result, [], "An empty list should be generated.")
    def test_parseLines_emptyString(self):
        """Test parseLines when passed an empty string."""
        unit = GcodeParser()

        result = []
        for item in unit.parseLines(""):
            result.append(item.fullText)

        self.assertEqual(result, [], "No lines should be parsed")
    def test_parameterItems_none(self):
        """Test parameterItems when the parameters attribute is None."""
        unit = GcodeParser()

        self.assertIsNone(unit.parameters, "The parameters should be None.")

        result = [item for item in unit.parameterItems()]

        self.assertEqual(result, [], "An empty list should be generated.")
    def test_parseLines_singleLine(self):
        """Test parseLines when passed a string containing a single line."""
        unit = GcodeParser()

        result = []
        for item in unit.parseLines("command 1"):
            result.append(item.fullText)

        self.assertEqual(result, ["command 1"], "The expected lines should be parsed")
    def test_parseLines_blankLine(self):
        """Test parseLines when passed a single line ending."""
        unit = GcodeParser()

        result = []
        for item in unit.parseLines("\n"):
            result.append(item.fullText)

        self.assertEqual(result, ["\n"], "The expected lines should be parsed")
    def test_parameterDict_getter_parametersNone(self):
        """Test the parameterDict getter when the parameters property is None."""
        unit = GcodeParser()
        unit._parameters = None  # pylint: disable=protected-access

        with mock.patch.object(unit, 'parameterItems') as mockParameterItems:
            result = unit.parameterDict

            mockParameterItems.assert_not_called()
            self.assertIsNone(result, "The result should be None")
    def test_parseLines_None(self):
        """Test parseLines parses the current source when passed None."""
        unit = GcodeParser()
        unit.source = "command 1"

        result = []
        for item in unit.parseLines(None):
            result.append(item.fullText)

        self.assertEqual(result, ["command 1"], "The expected commands should be parsed")
    def test_commandString_cached(self):
        """Test the commandString property when a value is already cached."""
        unit = GcodeParser()
        unit._commandString = "foo"  # pylint: disable=protected-access

        with mock.patch.object(unit, 'stringify') as mockStringify:
            result = unit.commandString

            mockStringify.assert_not_called()
            self.assertEqual(result, "foo", "The result should be the cached value.")
    def test_buildCommand_two_kwargs(self):
        """Test buildCommand when passed a GCode command and two kwargs."""
        unit = GcodeParser()

        result = unit.buildCommand("G0", X=10, Y=20)

        self.assertRegexpMatches(result, "^G0 ", "The returned command should start with 'G0 '")
        # Due to kwargs, order of arguments is not guaranteed, and also is not required
        self.assertRegexpMatches(result, " X10( |$)", "The returned command should contain ' X10'")
        self.assertRegexpMatches(result, " Y20( |$)", "The returned command should contain ' Y20'")
    def test_parameterItems_whitespace_labels_and_values(self):
        """Test that parameterItems permits whitespace between labels and values."""
        unit = GcodeParser()
        self.assertIsNone(unit.parameters, "The parameters should be None.")

        result = [item for item in unit.parameterItems("A  1 B C  2")]

        self.assertEqual(
            result, [("A", 1), ("B", None), ("C", 2), ("", "B C  2")],
            "The expected result should be generated."
        )
    def test_lineNumber_setter_none(self):
        """Test the lineNumber setter assigning None."""
        unit = GcodeParser()
        unit.parse("N1234 G0")
        self.assertEqual(unit.lineNumber, 1234, "The lineNumber should be 1234.")

        unit.lineNumber = None

        self.assertIsNone(unit.lineNumber, "The lineNumber should be None.")
        self.assertEqual(unit.gcode, "G0", "The gcode should be 'G0'.")
        self.assertEqual(unit.commandString, "G0", "The commandString should be 'G0'.")
    def test_stringify_lineNumberFalse_checksumFalse(self):
        """Test stringify when includeLineNumber=False and includeChecksum=False."""
        unit = GcodeParser()
        unit.parse("N123   G28")

        result = unit.stringify(includeLineNumber=False, includeChecksum=False)

        self.assertEqual(
            result, "G28",
            "The expected result should be returned"
        )
    def test_stringify_defaults(self):
        """Test stringify when no parameters are supplied."""
        unit = GcodeParser()

        unit.parse("   N0123   G028  X  *107   ; Comment   \r\n")

        result = unit.stringify()

        self.assertEqual(
            result, "   N123 G28 X *75 ; Comment   \r\n",
            "The result should be the normalized text"
        )
    def test_parameterItems_custom_string(self):
        """Test that parameterItems parses the given string when one is given."""
        unit = GcodeParser()

        self.assertIsNone(unit.parameters, "The parameters should be None.")

        result = [item for item in unit.parameterItems("A1BC2")]

        self.assertEqual(
            result, [("A", 1), ("B", None), ("C", 2), ("", "BC2")],
            "The expected result should be generated."
        )
    def test_parameterItems_nonAlphaFirstChar(self):
        """Test parameterItems when the first character is non-alphabetic."""
        unit = GcodeParser()

        self.assertIsNone(unit.parameters, "The parameters should be None.")

        result = [item for item in unit.parameterItems("12 A2")]

        self.assertEqual(
            result, [("A", 2), ("", "12 A2")],
            "The expected result should be generated."
        )
    def test_parseLines_offset(self):
        """Test parseLines when passed an offset value."""
        unit = GcodeParser()

        result = []
        for item in unit.parseLines("command 1\rcommand 2\rcommand 3", 10):
            result.append(item.fullText)

        self.assertEqual(
            result, ["command 2\r", "command 3"],
            "The expected lines should be parsed"
        )
    def test_stringify_includeEolFalse(self):
        """Test stringify when includeEol=False."""
        unit = GcodeParser()

        unit.parse("G 28\n")
        self.assertEqual(unit.eol, "\n", "The eol should be '\\n'")

        result = unit.stringify(includeEol=False)

        self.assertEqual(
            result, "G28",
            "The expected result should be returned"
        )