branch: elpa/adoc-mode
commit c709a481cf707bf8bef6402907a32452b8121466
Author: Bozhidar Batsov <[email protected]>
Commit: Bozhidar Batsov <[email protected]>

    Add a font-lock smoke test over a complex sample document
    
    Add test/resources/sample.adoc, a single construct-dense AsciiDoc document
    (all title levels, every delimited-block type incl. source blocks, tables,
    all list flavours, admonitions, the full inline-formatting set, macros,
    anchors, directives, breaks), and a smoke test that fontifies it and asserts
    font-lock runs without error, is idempotent, applies the expected faces, and
    exercises native code-block fontification. Guards against regressions that
    only surface on large, complex documents.
---
 test/adoc-mode-smoke-test.el   |  82 +++++++++++++++++
 test/adoc-mode-test-helpers.el |  23 +++++
 test/resources/sample.adoc     | 197 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 302 insertions(+)

diff --git a/test/adoc-mode-smoke-test.el b/test/adoc-mode-smoke-test.el
new file mode 100644
index 0000000000..4a46e11d6f
--- /dev/null
+++ b/test/adoc-mode-smoke-test.el
@@ -0,0 +1,82 @@
+;;; adoc-mode-smoke-test.el --- Font-lock smoke test for adoc-mode -*- 
lexical-binding: t; -*-
+
+;; Copyright © 2025-2026 Bozhidar Batsov
+
+;;; Commentary:
+
+;; A smoke test that fontifies a large, construct-dense real-world-style
+;; document (test/resources/sample.adoc) and checks that adoc-mode handles
+;; it without error, produces a stable (idempotent) fontification, and
+;; actually applies the expected faces.  This guards against regressions
+;; that only show up on complex documents (runaway multiline state,
+;; under-fontification, errors in keyword matchers).
+
+;;; Code:
+
+(require 'adoc-mode-test-helpers)
+
+(describe "adoc-mode font-lock smoke test"
+
+  (defun adoc-test--sample-buffer ()
+    "Return a fontified `adoc-mode' buffer with the sample document."
+    (let ((buf (generate-new-buffer " *adoc-sample*")))
+      (with-current-buffer buf
+        (insert-file-contents (adoc-test-resource "sample.adoc"))
+        (adoc-mode)
+        (font-lock-ensure))
+      buf))
+
+  (it "fontifies the whole sample document without error"
+    (let ((buf (adoc-test--sample-buffer)))
+      (unwind-protect
+          (with-current-buffer buf
+            ;; re-fontifying an already-fontified buffer must not error either
+            (expect (font-lock-ensure) :not :to-throw)
+            (expect (> (point-max) 1000) :to-be-truthy))
+        (kill-buffer buf))))
+
+  (it "produces a stable (idempotent) fontification"
+    (let ((buf (adoc-test--sample-buffer)))
+      (unwind-protect
+          (with-current-buffer buf
+            (cl-flet ((face-vector ()
+                        (let (acc (pos (point-min)))
+                          (while (< pos (point-max))
+                            (push (get-text-property pos 'face) acc)
+                            (setq pos (1+ pos)))
+                          (nreverse acc))))
+              (let ((faces-before (face-vector)))
+                (font-lock-flush)
+                (font-lock-ensure)
+                (expect (face-vector) :to-equal faces-before))))
+        (kill-buffer buf))))
+
+  (it "applies the expected faces across the document"
+    (let ((buf (adoc-test--sample-buffer)))
+      (unwind-protect
+          (with-current-buffer buf
+            (let ((faces (adoc-test-buffer-faces)))
+              ;; structural + inline + block + macro faces should all appear
+              (dolist (expected '(adoc-title-0-face
+                                  adoc-title-1-face
+                                  adoc-bold-face
+                                  adoc-emphasis-face
+                                  adoc-highlight-face
+                                  adoc-list-face
+                                  adoc-checkbox-face
+                                  adoc-table-face
+                                  adoc-anchor-face
+                                  adoc-complex-replacement-face
+                                  adoc-metadata-key-face
+                                  adoc-command-face
+                                  adoc-comment-face))
+                (expect (member expected (mapcar (lambda (f) (if (listp f) 
(car f) f))
+                                                 faces))
+                        :to-be-truthy))
+              ;; native code-block fontification should be present (ruby/shell)
+              (expect (seq-find (lambda (f) (and (listp f) (memq 
'adoc-native-code-face f)))
+                                faces)
+                      :not :to-be nil)))
+        (kill-buffer buf)))))
+
+;;; adoc-mode-smoke-test.el ends here
diff --git a/test/adoc-mode-test-helpers.el b/test/adoc-mode-test-helpers.el
index 25301f03bc..496d6d63b5 100644
--- a/test/adoc-mode-test-helpers.el
+++ b/test/adoc-mode-test-helpers.el
@@ -15,6 +15,29 @@
 (require 'adoc-mode)
 (require 'cl-lib)
 
+;;;; Test resources
+
+(defconst adoc-test-resources-directory
+  (expand-file-name
+   "resources"
+   (file-name-directory (or load-file-name buffer-file-name 
default-directory)))
+  "Absolute path to the test resources directory (test/resources/).")
+
+(defun adoc-test-resource (name)
+  "Return the absolute path of resource file NAME under test/resources/."
+  (expand-file-name name adoc-test-resources-directory))
+
+(defun adoc-test-buffer-faces ()
+  "Return the set of distinct `face' values present in the current buffer."
+  (let ((faces '())
+        (pos (point-min)))
+    (while (< pos (point-max))
+      (let ((f (get-text-property pos 'face)))
+        (when (and f (not (member f faces)))
+          (push f faces)))
+      (setq pos (1+ pos)))
+    faces))
+
 ;;;; String helpers
 
 (defun adoc-test--dedent (string)
diff --git a/test/resources/sample.adoc b/test/resources/sample.adoc
new file mode 100644
index 0000000000..8696c1fcc8
--- /dev/null
+++ b/test/resources/sample.adoc
@@ -0,0 +1,197 @@
+= Sample AsciiDoc Document
+:author: Bozhidar Batsov
+:email: [email protected]
+:toc: left
+:sample-attr: a reusable value
+
+This fixture exercises a broad cross-section of AsciiDoc constructs.  It is
+used by the font-lock smoke test to make sure a realistic, construct-dense
+document fontifies without error, stably, and with the expected faces.
+
+A reference to an attribute: {sample-attr}.
+
+== Inline formatting
+
+Constrained: *bold*, _italic_, `monospace`, #highlight#, and ^super^/~sub~.
+
+Unconstrained: **b**old, __i__talic, ##mark##ed.
+
+Passthroughs are not monospace: +plain text+ and ++unconstrained++, while a
++++raw +++ and $$literal$$ pass content through.
+
+Curved quotes: "`a quotation`" and '`an aside`'.
+
+Escapes stay literal: \*not bold* and \`not code`.
+
+Roles layer on quotes: [.line-through]#struck#, [.underline]#lined#,
+[.overline]#over#.
+
+Replacements: (C), (R), (TM), an ellipsis ..., and arrows -> => <- <=.
+
+A forced line break here +
+continues on the next line.
+
+== Section level one
+
+=== Section level two
+
+==== Section level three
+
+===== Section level four
+
+====== Section level five
+
+[#custom-id]
+== Section with an id shorthand
+
+See <<custom-id>> and <<custom-id,a caption>> and xref:other.adoc[an xref].
+
+[[block-anchor]]
+A block anchor target, referenced via <<block-anchor>>.
+
+[[[bib-entry]]] A bibliography entry.
+
+== Lists
+
+.An unordered list
+* level one
+** level two
+*** level three
+- a dash bullet
+
+.An ordered list
+. implicit one
+. implicit two
+.. implicit nested
+1. explicit arabic
+a. explicit alpha
+
+.A checklist
+* [ ] todo item
+* [x] done item
+* [*] also done
+
+.A description list
+First term:: Its definition.
+Second term:: Another definition.
+
+.A callout list
+[source,ruby]
+----
+puts "hello" # <1>
+----
+<1> Prints a greeting.
+
+== Admonitions
+
+NOTE: This is an admonition paragraph.
+
+[WARNING]
+====
+This is an admonition block with a delimited body.
+====
+
+== Delimited blocks
+
+.A listing block
+----
+plain listing, no language
+----
+
+.A Ruby source block
+[source,ruby]
+----
+def greet(name)
+  # say hello
+  puts "Hello, #{name}!"
+end
+----
+
+.A shell source block
+[source,shell]
+----
+rubocop --auto-correct --format simple
+----
+
+.A literal block
+....
+literal text, rendered verbatim
+....
+
+.A quote block
+[quote, Anonymous]
+____
+The body of a quotation.
+____
+
+.A sidebar
+****
+An aside in a sidebar.
+****
+
+.A passthrough block
+++++
+<custom-html>passed through</custom-html>
+++++
+
+.An open block
+--
+An open block body.
+--
+
+////
+A comment block that should be ignored entirely.
+////
+
+// a single-line comment
+
+== Tables
+
+.A small table
+[cols="1,1",options="header"]
+|===
+| Name | Value
+
+| alpha
+| one
+
+| beta
+| two
+|===
+
+== Macros
+
+A link: https://example.com/page[the docs] and a bare URL
+https://example.com/raw and an email [email protected].
+
+An image: image:logo.png[Logo,32,32] and a block image:
+
+image::diagram.png[Architecture diagram]
+
+A footnote.footnote:[The footnote text.] and a UI macro kbd:[C-c C-c],
+a button btn:[OK], a menu menu:File[Save As], and an icon icon:heart[2x].
+
+Math via STEM: stem:[a^2 + b^2 = c^2], latexmath:[E = mc^2],
+asciimath:[sqrt(x)].
+
+An index term (((indexing, terms))) appears here.
+
+A passthrough macro: pass:[<b>raw</b>].
+
+== Directives
+
+ifdef::sample-attr[Shown when the attribute is set.]
+
+include::partial.adoc[]
+
+== Breaks
+
+A thematic break:
+
+'''
+
+A page break:
+
+<<<
+
+The end.

Reply via email to