From d5b6fc0727c230661492388820e3dc98b6f3573b Mon Sep 17 00:00:00 2001 From: Esko Luontola Date: Sat, 2 Dec 2023 00:36:36 +0200 Subject: [PATCH] Regression tests against method code size growing too quickly See weavejester/hiccup#205 --- test/hiccup2/optimizations_test.clj | 90 +++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 test/hiccup2/optimizations_test.clj diff --git a/test/hiccup2/optimizations_test.clj b/test/hiccup2/optimizations_test.clj new file mode 100644 index 0000000..156104c --- /dev/null +++ b/test/hiccup2/optimizations_test.clj @@ -0,0 +1,90 @@ +(ns hiccup2.optimizations-test + (:require [clojure.pprint :as pp] + [clojure.test :refer :all] + [clojure.walk :as walk] + [hiccup2.core :as h])) + +(defn- count-forms [data] + (let [*counter (atom 0)] + (walk/postwalk (fn [x] + (when (seq? x) + (swap! *counter inc)) + x) + data) + @*counter)) + +(deftest test-count-forms + (is (= 1 (count-forms '()))) + (is (= 3 (count-forms '(if (number? x) (inc x) x))))) + +(deftest method-code-size + ;; With Hiccup 2.0.0-RC2, it was easy to cause the hiccup2.core/html macro to generate so much bytecode + ;; that it would go over the 64KB limit of how much bytecode one Java method may contain. It would crash + ;; the Clojure compiler with a "Method code too large!" exception. These are a regression tests for that. + ;; See https://github.com/weavejester/hiccup/issues/205 + + (testing "static elements should be concatenated to one string, also when they have dynamic sibling elements" + (let [baseline (walk/macroexpand-all + `(h/html [:div + [:p] + (identity nil) + [:p]])) + pathological (walk/macroexpand-all + `(h/html [:div + [:p] [:p] [:p] [:p] [:p] + (identity nil) + [:p] [:p] [:p] [:p] [:p]]))] + #_(pp/pprint pathological) + (is (= (count-forms baseline) + (count-forms pathological))))) + + (testing "code size should grow O(n), instead of O(n^2), as more dynamic first-child elements are added" + (let [example-0 (walk/macroexpand-all + `(h/html [:div + [:div + [:div + [:div + [:div]]]]])) + example-1 (walk/macroexpand-all + `(h/html [:div (identity nil) + [:div + [:div + [:div + [:div]]]]])) + example-2 (walk/macroexpand-all + `(h/html [:div (identity nil) + [:div (identity nil) + [:div + [:div + [:div]]]]])) + example-3 (walk/macroexpand-all + `(h/html [:div (identity nil) + [:div (identity nil) + [:div (identity nil) + [:div + [:div]]]]])) + example-4 (walk/macroexpand-all + `(h/html [:div (identity nil) + [:div (identity nil) + [:div (identity nil) + [:div (identity nil) + [:div]]]]])) + example-5 (walk/macroexpand-all + `(h/html [:div (identity nil) + [:div (identity nil) + [:div (identity nil) + [:div (identity nil) + [:div (identity nil)]]]]])) + examples [example-0 + example-1 + example-2 + example-3 + example-4 + example-5] + diffs (->> examples + (map count-forms) + (partition 2 1) + (map (fn [[a b]] (- b a))))] + #_(pp/pprint example-3) + (is (< (apply max diffs) + (* 1.1 (apply min diffs)))))))