From a31675044e10876c1a68226ac7f89d3e8f14ecae Mon Sep 17 00:00:00 2001 From: Vladyslav Kuksiuk Date: Thu, 2 Jul 2026 15:45:37 +0200 Subject: [PATCH] Provide parser state test. --- embedding/parsing/state_test.go | 162 ++++++++++++++++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 embedding/parsing/state_test.go diff --git a/embedding/parsing/state_test.go b/embedding/parsing/state_test.go new file mode 100644 index 0000000..fff4377 --- /dev/null +++ b/embedding/parsing/state_test.go @@ -0,0 +1,162 @@ +// Copyright 2026, TeamDev. All rights reserved. +// +// Redistribution and use in source and/or binary forms, with or without +// modification, must retain the above copyright notice and the following +// disclaimer. +// +// 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. + +package parsing_test + +import ( + "errors" + "os" + "path/filepath" + "strings" + + "embed-code/embed-code-go/configuration" + "embed-code/embed-code-go/embedding/parsing" + _type "embed-code/embed-code-go/type" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("Parser states", func() { + It("should consume a multiline instruction before opening an embedding fence", func() { + config := configuration.NewConfiguration() + context := newStateContext( + "", + " ````java", + "old source", + " ````", + ) + + Expect(parsing.EmbedInstruction.Recognize(context)).Should(BeTrue()) + Expect(parsing.EmbedInstruction.Accept(&context, config)).Should(Succeed()) + + Expect(context.CurrentIndex()).Should(Equal(3)) + Expect(context.EmbeddingInstruction).ShouldNot(BeNil()) + Expect(context.EmbeddingInstruction.CodeFile).Should(Equal("Example.java")) + Expect(context.EmbeddingInstruction.DocumentationLine).Should(Equal(1)) + Expect(context.GetResult()).Should(Equal([]string{ + "", + })) + + Expect(parsing.CodeFenceStart.Recognize(context)).Should(BeTrue()) + Expect(parsing.CodeFenceStart.Accept(&context, config)).Should(Succeed()) + + Expect(context.CurrentIndex()).Should(Equal(4)) + Expect(context.CodeFenceStarted).Should(BeTrue()) + Expect(context.CodeFenceMarker).Should(Equal("````")) + Expect(context.CodeFenceIndentation).Should(Equal(4)) + Expect(context.CurrentEmbedding().SourceStartIndex).Should(Equal(3)) + }) + + It("should recognize only matching embedding fence endings", func() { + context := newStateContext( + " ```", + "```", + " ````", + " ``` language", + ) + context.CodeFenceStarted = true + context.CodeFenceMarker = "```" + context.CodeFenceIndentation = 4 + + Expect(parsing.CodeFenceEnd.Recognize(context)).Should(BeTrue()) + + context.ToNextLine() + Expect(parsing.CodeFenceEnd.Recognize(context)).Should(BeFalse()) + + context.ToNextLine() + Expect(parsing.CodeFenceEnd.Recognize(context)).Should(BeTrue()) + + context.ToNextLine() + Expect(parsing.CodeFenceEnd.Recognize(context)).Should(BeFalse()) + }) + + It("should report a malformed instruction when EOF is reached before the tag closes", func() { + config := configuration.NewConfiguration() + context := newStateContext( + "preface", + "` tag is not closed")) + Expect(context.ReachedEOF()).Should(BeTrue()) + Expect(context.GetResult()).Should(Equal([]string{ + "preface", + "", + "```java", + "old source", + "```", + ) + Expect(parsing.EmbedInstruction.Accept(&context, config)).Should(Succeed()) + Expect(parsing.CodeFenceStart.Accept(&context, config)).Should(Succeed()) + Expect(parsing.CodeSampleLine.Accept(&context, config)).Should(Succeed()) + + Expect(parsing.CodeFenceEnd.Recognize(context)).Should(BeTrue()) + Expect(parsing.CodeFenceEnd.Accept(&context, config)).Should(Succeed()) + + Expect(context.CodeFenceStarted).Should(BeFalse()) + Expect(context.EmbeddingInstruction).Should(BeNil()) + Expect(context.CurrentEmbedding().SourceEndIndex).Should(Equal(3)) + Expect(context.GetResult()).Should(Equal([]string{ + "", + "```java", + "class Example {}", + "```", + })) + }) +}) + +// newStateContext builds a parser context from in-memory source lines. +func newStateContext(lines ...string) parsing.Context { + docPath := filepath.Join(GinkgoT().TempDir(), "doc.md") + err := os.WriteFile(docPath, []byte(strings.Join(lines, "\n")), 0600) + if err != nil { + Fail("unexpected error while writing parser state fixture: " + err.Error()) + } + context, err := parsing.NewContext(docPath) + if err != nil { + Fail("unexpected error while creating parser state context: " + err.Error()) + } + + return context +}