branch: elpa/flamegraph
commit a7629b2b5812ae804446da3e39bae4b0ea5a5e3a
Author: Dmitry Gutov <[email protected]>
Commit: Dmitry Gutov <[email protected]>

    Add tests for the calls-tree and describe heading
---
 test/flamegraph-tests.el | 131 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 131 insertions(+)

diff --git a/test/flamegraph-tests.el b/test/flamegraph-tests.el
index ad63da6c88..a3671d174f 100644
--- a/test/flamegraph-tests.el
+++ b/test/flamegraph-tests.el
@@ -242,5 +242,136 @@ callees outside it must still be found."
       (should (text-property-any 0 (length s)
                                  'face 'flamegraph-call-site s)))))
 
+;;; Calls-tree rendering — `flamegraph--insert-call-tree'
+
+(defun flamegraph-test--render-call-tree (node ref)
+  "Render NODE's call tree (REF samples for percent scaling) and return
+the resulting string."
+  (with-temp-buffer
+    (flamegraph--insert-call-tree node ref ref 0 nil)
+    (buffer-string)))
+
+(defun flamegraph-test--line-with (str lines)
+  "Return the first LINES element containing STR as a whole symbol."
+  (cl-find-if (lambda (l)
+                (string-match-p (concat "\\_<" (regexp-quote str) "\\_>") l))
+              lines))
+
+(defun flamegraph-test--indent (line)
+  "Leading-space count of LINE."
+  (if (string-match "\\` +" line) (length (match-string 0 line)) 0))
+
+(ert-deftest flamegraph-test-call-tree-children-sorted-by-count ()
+  "Direct children appear sorted by count, descending."
+  (let* ((bar (profiler-make-calltree :entry 'bar :count 30))
+         (baz (profiler-make-calltree :entry 'baz :count 70))
+         (foo (profiler-make-calltree :entry 'foo :count 100
+                                      :children (list bar baz))))
+    (let* ((out (flamegraph-test--render-call-tree foo 100))
+           (baz-pos (string-match "\\_<baz\\_>" out))
+           (bar-pos (string-match "\\_<bar\\_>" out)))
+      (should baz-pos)
+      (should bar-pos)
+      (should (< baz-pos bar-pos)))))
+
+(ert-deftest flamegraph-test-call-tree-skip-through-expanded-indented ()
+  "Skip-through children render their sub-children indented underneath."
+  (let* ((real-child (profiler-make-calltree :entry 'real-fn :count 80))
+         (if-node    (profiler-make-calltree :entry 'if :count 80
+                                             :children (list real-child)))
+         (other      (profiler-make-calltree :entry 'other-fn :count 20))
+         (root (profiler-make-calltree :entry 'root :count 100
+                                       :children (list if-node other))))
+    (let* ((out (flamegraph-test--render-call-tree root 100))
+           (lines (split-string out "\n" t))
+           (if-line    (flamegraph-test--line-with "if" lines))
+           (rf-line    (flamegraph-test--line-with "real-fn" lines))
+           (other-line (flamegraph-test--line-with "other-fn" lines)))
+      (should if-line)
+      (should rf-line)
+      (should other-line)
+      ;; real-fn is indented deeper than its skip-through parent.
+      (should (> (flamegraph-test--indent rf-line)
+                 (flamegraph-test--indent if-line)))
+      ;; The non-skip sibling sits at the same depth as `if'.
+      (should (= (flamegraph-test--indent if-line)
+                 (flamegraph-test--indent other-line))))))
+
+(ert-deftest flamegraph-test-call-tree-non-skip-not-expanded ()
+  "A non-skip child's own descendants are NOT inlined into the tree."
+  (let* ((grand (profiler-make-calltree :entry 'grand :count 50))
+         (kid   (profiler-make-calltree :entry 'kid :count 50
+                                        :children (list grand)))
+         (root (profiler-make-calltree :entry 'root :count 100
+                                       :children (list kid))))
+    (let* ((out (flamegraph-test--render-call-tree root 100))
+           (lines (split-string out "\n" t)))
+      (should (flamegraph-test--line-with "kid" lines))
+      (should-not (flamegraph-test--line-with "grand" lines)))))
+
+(ert-deftest flamegraph-test-call-tree-percent-scaled-to-ref ()
+  "Sub-children's percentage uses REF (the described frame's count),
+not their immediate parent — so all percents add up under that frame."
+  ;; root 1000, if 500, fn 250 → fn's printed share is 25%, not 50%.
+  (let* ((fn (profiler-make-calltree :entry 'fn :count 250))
+         (if-node (profiler-make-calltree :entry 'if :count 500
+                                          :children (list fn)))
+         (root (profiler-make-calltree :entry 'root :count 1000
+                                       :children (list if-node))))
+    (let* ((out (flamegraph-test--render-call-tree root 1000))
+           (fn-line (flamegraph-test--line-with
+                     "fn" (split-string out "\n" t))))
+      (should fn-line)
+      (should (string-match-p "(25%)" fn-line))
+      (should-not (string-match-p "(50%)" fn-line)))))
+
+(ert-deftest flamegraph-test-call-tree-rows-are-describe-buttons ()
+  "Every rendered row is a clickable describe-button."
+  (let* ((bar (profiler-make-calltree :entry 'bar :count 1))
+         (foo (profiler-make-calltree :entry 'foo :count 1
+                                      :children (list bar))))
+    (with-temp-buffer
+      (flamegraph--insert-call-tree foo 1 1 0 nil)
+      (goto-char (point-min))
+      (let ((b (next-button (point))))
+        (should b)
+        (should (eq (button-type b) 'help-xref))
+        (should (eq (button-get b 'help-function)
+                    #'flamegraph--describe-frame))))))
+
+;;; Heading rendering in `flamegraph--describe-frame'
+
+(defun flamegraph-test--describe-help (node)
+  "Render NODE in the help buffer via `flamegraph--describe-frame' and
+return that buffer's contents (a propertized string)."
+  (require 'help-mode)
+  (flamegraph--describe-frame node (profiler-calltree-count node) nil)
+  (with-current-buffer (help-buffer) (buffer-string)))
+
+(ert-deftest flamegraph-test-describe-symbol-heading-is-help-button ()
+  "For an fboundp symbol entry the heading is a `help-function' button."
+  (let ((node (profiler-make-calltree :entry 'flamegraph-describe
+                                      :count 10)))
+    (flamegraph-test--describe-help node)
+    (with-current-buffer (help-buffer)
+      (goto-char (point-min))
+      (let ((b (button-at (point))))
+        (should b)
+        (should (eq (button-type b) 'help-function))
+        (should (equal (button-get b 'help-args)
+                       '(flamegraph-describe)))))))
+
+(ert-deftest flamegraph-test-describe-string-heading-is-plain ()
+  "For a non-symbol entry, the heading is plain text (no button at point 1)."
+  (let ((node (profiler-make-calltree
+               :entry "redisplay_window:/tmp/xdisp.c:1"
+               :count 10)))
+    (flamegraph-test--describe-help node)
+    (with-current-buffer (help-buffer)
+      (goto-char (point-min))
+      (should-not (and (button-at (point))
+                       (eq (button-type (button-at (point)))
+                           'help-function))))))
+
 (provide 'flamegraph-tests)
 ;;; flamegraph-tests.el ends here

Reply via email to