summaryrefslogtreecommitdiff
path: root/test/humpfm
diff options
context:
space:
mode:
authoryo momma <shutup@oops.wtf>2026-02-04 19:25:03 +0000
committeryo momma <shutup@oops.wtf>2026-02-04 19:25:03 +0000
commit75cf2a9c3cd925d2d8de4b5ef98bdd55c29e9861 (patch)
tree457640db50cd62bac0649119abed37c3bdeb2fe7 /test/humpfm
parentad4d74ea45be6b227027588f6515fb22fafc335a (diff)
Rebrand: rename dump assets/templates to hump
Diffstat (limited to 'test/humpfm')
-rwxr-xr-xtest/humpfm/load_test.clj112
-rwxr-xr-xtest/humpfm/millstone.clj148
2 files changed, 260 insertions, 0 deletions
diff --git a/test/humpfm/load_test.clj b/test/humpfm/load_test.clj
new file mode 100755
index 0000000..3196f1c
--- /dev/null
+++ b/test/humpfm/load_test.clj
@@ -0,0 +1,112 @@
+(ns humpfm.load-test
+ (:import org.postgresql.ds.PGPoolingDataSource)
+ (:use clojure.contrib.json.read
+ clojure.contrib.json.write
+ clojure.contrib.seq-utils
+ clojure.contrib.sql
+ humpfm.millstone))
+
+
+(let [db-host "localhost"
+ db-name "humpfm"
+ db-user "postgres"
+ db-pass "root"]
+ (def *db* {:datasource (doto (new PGPoolingDataSource)
+ (.setServerName db-host)
+ (.setDatabaseName db-name)
+ (.setUser db-user)
+ (.setPassword db-pass)
+ (.setMaxConnections 3))}))
+
+(def userlist-query "
+select u.nick, u.hash
+from users u, messages m
+where u.user_id = m.user_id
+and u.user_id not in
+ (select user_id from mutes where (set_on + duration < now()))
+group by u.nick, u.hash
+having count(*) > 50
+order by count(*) desc
+")
+
+(print "Fetching userlist: ")
+(def userlist (time
+ (with-connection *db*
+ (with-query-results rs [userlist-query]
+ (doall rs)))))
+
+
+(def sample-messages-query "
+select content
+from messages
+order by random()
+limit 100
+")
+
+(print "Fetching messages: ")
+(def message-contents (time
+ (with-connection *db*
+ (with-query-results rs [sample-messages-query]
+ (doall (map :content rs))))))
+
+(defn login-client []
+ (let [user-info (rand-elt userlist)
+ params (select-keys user-info [:nick :hash])]
+ (do-setup-request! "/login"
+ :params params
+ :method "GET")))
+
+(defn rand-nick []
+ (:nick (rand-elt userlist)))
+
+(defn load-profile []
+ (do-request! (str "/" (rand-nick))
+ :label "profile"))
+
+(defn load-popular-profile []
+ (do-request! (str "/" (rand-nick))
+ :label "popular-profile"))
+
+(defn load-popular-popular []
+ (do-request! (str "/" (rand-elt ["ryder" "jeanette"]) "/popular")
+ :label "popular-popular"))
+
+(defn load-popular []
+ (do-request! (str "/" (rand-nick) "/popular")
+ :label "popular"))
+
+(defn chat []
+ (do-request! "/test/chat"))
+
+(defn refresh []
+ (let [params {:since (- (System/currentTimeMillis) 2000)
+ :room "test"}]
+ (do-request! "/refresh"
+ :params params
+ :method "GET")
+ (Thread/sleep 1000)))
+
+(defn post-msg []
+ (let [params {:content (rand-elt message-contents)
+ :room "test"}]
+ (do-request! "/msg"
+ :params params
+ :method "POST")))
+
+(def test-spec {:server "http://localhost:8080"
+ :clients 75
+ :requests 10000
+ :max-latency 1000
+ :setup-func login-client
+ :funcs [[55 refresh]
+ [10 load-popular]
+ [10 load-profile]
+ [3 load-popular-popular]
+ [3 load-popular-profile]
+ [3 chat]
+ [5 post-msg]]
+ :frequency 1
+ })
+
+(grind! test-spec)
+(System/exit 0) \ No newline at end of file
diff --git a/test/humpfm/millstone.clj b/test/humpfm/millstone.clj
new file mode 100755
index 0000000..095abed
--- /dev/null
+++ b/test/humpfm/millstone.clj
@@ -0,0 +1,148 @@
+(ns humpfm.millstone
+ (:use clojure.contrib.def
+ clojure.contrib.seq-utils
+ clojure-http.client))
+
+(def *spec*)
+(def *cookies*)
+(def *results*)
+
+(def printer (agent nil))
+
+(defn log [& args]
+ (send printer (fn [_] (apply println args))))
+
+(defmacro with-timing [e]
+ `(let [s# (System/nanoTime)
+ r# ~e
+ f# (System/nanoTime)]
+ [(float (/ (- f# s#) 1e6)) r#]))
+
+(defn do-base-request [server path method cookies params]
+ (let [method (.toUpperCase method)
+ url (str server path)]
+ (if (= method "GET")
+ (request (add-query-params url params) method nil cookies)
+ (request url method nil cookies params))))
+
+(defnk do-setup-request! [path
+ :params nil
+ :method "GET"]
+ (let [res (do-base-request (:server *spec*) path method *cookies* params)]
+ (if (:cookies res)
+ (set! *cookies* (merge *cookies* (:cookies res))))
+ res))
+
+(defn- success? [resp]
+ (= (:code resp) 200))
+
+(defnk do-request! [path
+ :label nil
+ :params nil
+ :method "GET"]
+ (let [[ms resp] (with-timing
+ (try (do-base-request (:server *spec*)
+ path
+ method
+ *cookies*
+ params)
+ (catch Exception e
+ (log (format "Exception for %s - %s" path (.getMessage e)))
+ {:code "?" :msg "EXCEPTION"})))
+ result {:path path
+ :label (or label path)
+ :ms ms
+ :error (if (success? resp) nil [(:code resp) (:msg resp)])}]
+ (if (= (rand-int 100) 1)
+ (log (format "%s - %.02fms" path ms)))
+ (dosync
+ (commute *results* conj result))
+ resp))
+
+(defn build-client! [spec client-id]
+ (binding [*cookies* {}]
+ (if (:setup-func spec)
+ ((:setup-func spec)))
+ {:client-id client-id
+ :cookies *cookies*}))
+
+(defn join [s lst]
+ (apply str (interpose s lst)))
+
+(defn sum [nums]
+ (reduce + nums))
+
+(defn avg [nums]
+ (float (/ (sum nums) (count nums))))
+
+(defn print-run-results [spec results elapsed-ms]
+ (log (format "\nResults\n--------\n%s requests in %.02fs (%.02f r/s)\n%s clients"
+ (count results)
+ (/ elapsed-ms 1000)
+ (/ (count results) (/ elapsed-ms 1000))
+ (:clients spec)))
+ (let [avg-in-req (/ (sum (map :ms results)) (:clients spec))]
+ (log (format "\nTime spent in requests (per worker): %.02fs (%.02f%%)"
+ (/ avg-in-req 1000)
+ (* (/ avg-in-req elapsed-ms) 100))))
+ (doseq [[label rs] (group-by :label results)]
+ (let [nums (map :ms rs)
+ errors (filter identity (map :error rs))]
+ (log (format "\n%s:\n#: %s\nmin: %.02f ms\navg: %.02f ms\nmax: %.02f ms"
+ label
+ (count rs)
+ (apply min nums)
+ (avg nums)
+ (apply max nums)))
+ (if-let [threshhold (:max-latency spec)]
+ (let [timeouts (count (filter #(> % threshhold)
+ (map :ms rs)))]
+ (if (not (zero? timeouts))
+ (log (format "timeouts: %s (> %sms)" timeouts threshhold)))))
+ (if (not (empty? errors))
+ (log (format "errors: %s (%s)"
+ (count errors)
+ (join " " (sort (set errors))))))))
+ (log ""))
+
+(defn build-func-list [routes]
+ (flatten (for [[n f] routes]
+ (repeat n f))))
+
+(def num-updates 10)
+
+(defn update-markers [reqs num]
+ (set (map #(int (* % (/ reqs num))) (range 1 (inc num)))))
+
+(defn grind! [spec]
+ (binding [*spec* spec]
+ (let [clients (doall
+ (for [id (range (:clients spec))]
+ (build-client! spec id)))
+ reqs (:requests spec)
+ funcs (build-func-list (:funcs spec))
+ results (ref [])
+ counter (atom 0)
+ update-on (update-markers reqs num-updates)
+ threads (doall
+ (for [c clients]
+ (Thread.
+ (fn []
+ (binding [*spec* spec
+ *cookies* (:cookies c)
+ *results* results]
+ (loop []
+ (let [c (swap! counter inc)]
+ (if (<= c reqs)
+ (do ((rand-elt funcs))
+ (if (contains? update-on c)
+ (log (format "Completed %s requests" c)))
+ (recur))))))))))]
+ (log "Finished setup")
+ (let [[elapsed _] (with-timing
+ (do
+ (doseq [t threads]
+ (.start t))
+ (doseq [t threads]
+ (.join t))))]
+ (print-run-results spec @results elapsed)))))