Miguel de Benito Delgado 8eff426479 🐛 Make renderer benchmark diagnostics bounded and resilient
Retain build failure occurrences in failed manifests and preserve the
manifest-write error alongside the original preparation failure.

Bound diagnostic text, escape terminal controls, and keep Unicode pairs
intact. Validate optional traces and partial evidence before encoding, with
explicit diagnostics for unsupported or excessive data.

Preserve completed phase evidence and owner cleanup when bridge encoding
fails. Retain raw numeric observations, including NaN and infinities, and
keep the newest log-tail evidence after escaping.

AI-assisted-by: gpt-6.1-sol
2026-11-11 15:27:07 +01:00

162 lines
7.6 KiB
Clojure

;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS SUBSIDIARY SL
(ns performance-tests.render-wasm.log-test
(:require
["fs" :as fs]
["node:stream" :as stream]
["node:timers/promises" :as timers]
["os" :as os]
["path" :as path]
[benches.render-wasm.node.log :as log]
[cljs.test :as t :include-macros true]
[clojure.string :as str]))
(def temporary-root (atom nil))
(t/use-fixtures :each
{:before #(reset! temporary-root (fs/mkdtempSync (path/join (os/tmpdir) "bench-log-")))
:after (fn []
(log/configure {})
(fs/rmSync @temporary-root #js {:recursive true :force true}))})
(defn- capture
[]
(let [text (atom "")
out (stream/Writable.
#js {:write (fn [chunk _encoding done]
(swap! text str (.toString chunk))
(done))})]
{:stream out :text text}))
(t/deftest levels-filter-console-and-retain-every-line-in-the-file
(doseq [[level expected] [[:warn "! warning\n✖ failure\n"]
[:info "Phase\n attempt 1/1 completed\n! warning\n✖ failure\n"]
[:debug "Phase\n attempt 1/1 completed\n· raw child\n! warning\n✖ failure\n"]]]
(let [stdout (capture)
stderr (capture)
file (path/join @temporary-root (name level))]
(log/configure {:level level :color :never
:stdout (:stream stdout) :stderr (:stream stderr)})
(binding [log/*log-path* file]
(log/info "Phase")
(log/info " attempt 1/1 completed")
(log/debug "raw child")
(log/warn "warning")
(log/error {:message "failure" :tail? false})
(log/result "Run saved"))
(t/is (= "Run saved\n" @(:text stdout)))
(t/is (= (str/replace expected "✖ failure\n" (str "✖ failure\n log " file "\n"))
@(:text stderr)))
(t/is (= (str "Phase\n attempt 1/1 completed\nraw child\nwarning\n✖ failure\n log "
file "\nRun saved\n")
(fs/readFileSync file "utf8"))))))
(t/deftest colors-apply-to-console-headings-and-preserve-indented-progress
(doseq [[mode tty no-color term colored?]
[[:always false "1" "dumb" true] [:never true "" "xterm" false]
[:auto false "" "xterm" false] [:auto true "" "xterm" true]
[:auto true "1" "xterm" false] [:auto true "" "dumb" false]]]
(let [stdout (capture)
stderr (capture)
file (path/join @temporary-root (str (random-uuid)))
env (.-env js/process)
old-no-color (.-NO_COLOR env)
old-term (.-TERM env)]
(try
(set! (.-NO_COLOR env) no-color)
(set! (.-TERM env) term)
(set! (.-isTTY ^js (:stream stderr)) tty)
(log/configure {:color mode :stdout (:stream stdout) :stderr (:stream stderr)})
(binding [log/*log-path* file]
(log/info "Phase\n progress")
(log/result "Output /tmp/run"))
(t/is (= colored? (str/includes? @(:text stderr) "\u001b[")))
(t/is (= "Phase\n progress\n"
(str/replace @(:text stderr) #"\u001b\[[0-9;]*m" "")))
(t/is (= "Output /tmp/run\n" @(:text stdout)))
(t/is (= "Phase\n progress\nOutput /tmp/run\n" (fs/readFileSync file "utf8")))
(finally
(if (nil? old-no-color) (js-delete env "NO_COLOR") (set! (.-NO_COLOR env) old-no-color))
(if (nil? old-term) (js-delete env "TERM") (set! (.-TERM env) old-term)))))))
(t/deftest ^:async nested-tasks-route-lines-to-their-own-files-on-success-and-failure
(let [parent (path/join @temporary-root "comparison.log")
child (path/join @temporary-root "build.log")
output (capture)]
(log/configure {:level :warn :stdout (:stream output) :stderr (:stream output)})
(binding [log/*log-path* parent]
(log/debug "comparison before")
(await ((^:async fn []
(binding [log/*log-path* child]
(await (timers/setImmediate))
(log/debug "child success")))))
(log/debug "comparison between")
(try
(await ((^:async fn []
(binding [log/*log-path* child]
(await (timers/setImmediate))
(log/debug "child failure")
(throw (js/Error. "failed"))))))
(catch :default _ (log/debug "comparison after"))))
(t/is (= "comparison before\ncomparison between\ncomparison after\n"
(fs/readFileSync parent "utf8")))
(t/is (= "child success\nchild failure\n" (fs/readFileSync child "utf8")))))
(t/deftest console-diagnostics-escape-controls-and-retain-the-log-evidence
(let [file (path/join @temporary-root "controls.log")
output (capture)
message (str "work failed\u001b\u0007" (apply str (repeat 2000 "x")))]
(log/configure {:color :never :stderr (:stream output)})
(binding [log/*log-path* file]
(log/warn message))
(t/is (= (str message "\n") (fs/readFileSync file "utf8")))
(t/is (str/includes? @(:text output) "\\u001b\\u0007"))
(t/is (< (count @(:text output)) 1100))
(doseq [separator ["" "\n"]]
(reset! (:text output) "")
(fs/writeFileSync file (str (apply str (repeat 2000 "\u0007")) separator "FATAL: compiler failed\n") "utf8")
(log/error {:message "tail evidence" :tail-log file})
(t/is (str/includes? @(:text output) "FATAL: compiler failed"))
(t/is (every? #(let [code (.charCodeAt % 0)]
(or (= code 10) (and (>= code 32) (or (< code 127) (> code 159)))))
(seq @(:text output)))))))
(t/deftest error-names-tail-source-and-bounds-evidence-before-appending
(let [file (path/join @temporary-root "comparison.log")
child (path/join @temporary-root "build.log")
output (capture)]
(fs/writeFileSync child (str "excluded\n" (apply str (repeat 9000 "x")) "\n"
(str/join "\n" (map #(str "line-" %) (range 30)))) "utf8")
(log/configure {:color :never :stderr (:stream output)})
(binding [log/*log-path* file]
(log/error {:message "build failed" :fields [[:phase :renderer] [:build "id"]]
:tail-log child}))
(let [expected (str "✖ build failed\n phase :renderer\n build id\n"
" log " file "\n tail-log " child "\n tail\n"
(str/join "\n" (map #(str " line-" %) (range 10 30))) "\n")]
(t/is (= expected @(:text output)))
(t/is (= expected (fs/readFileSync file "utf8"))))
(fs/writeFileSync child (apply str (repeat 10000 "z")) "utf8")
(reset! (:text output) "")
(binding [log/*log-path* file]
(log/error {:message "bounded" :tail-log child}))
(t/is (= 8192 (count (re-find #"z+$" (str/trim @(:text output))))))
(fs/writeFileSync file "last line\n" "utf8")
(reset! (:text output) "")
(binding [log/*log-path* file]
(log/error {:message "same source"}))
(t/is (= (str "✖ same source\n log " file "\n tail\n last line\n")
@(:text output)))))
(t/deftest error-reporting-survives-unreadable-tail-and-file-append-failure
(let [output (capture)]
(log/configure {:color :never :stderr (:stream output)})
(binding [log/*log-path* @temporary-root]
(log/error {:message "original failure" :tail-log (path/join @temporary-root "missing")}))
(t/is (str/starts-with? @(:text output) "✖ original failure\n"))
(t/is (str/includes? @(:text output) @temporary-root))))