babashka/test/babashka/reify_test.clj
Michiel Borkent 267646fbe7 Fix gc test
2022-09-09 18:13:40 +02:00

155 lines
5.2 KiB
Clojure

(ns babashka.reify-test
(:require
[babashka.test-utils :as test-utils]
[clojure.edn :as edn]
[clojure.string :as str]
[clojure.test :as test :refer [deftest is testing]]))
(defn bb [input & args]
(edn/read-string
{:readers *data-readers*
:eof nil}
(apply test-utils/bb (when (some? input) (str input)) (map str args))))
(deftest file-filter-test
(is (true? (bb nil "
(def filter-obj (reify java.io.FileFilter
(accept [this f] (prn (.getPath f)) true)))
(def filename-filter-obj
(reify java.io.FilenameFilter
(accept [this f name] (prn name) true)))
(def s1 (with-out-str (.listFiles (clojure.java.io/file \".\") filter-obj)))
(def s2 (with-out-str (.listFiles (clojure.java.io/file \".\") filename-filter-obj)))
(and (pos? (count s1)) (pos? (count s2)))"))))
(deftest reify-multiple-arities-test
(testing "ILookup"
(is (= ["->:foo" 10]
(bb nil "
(def m (reify clojure.lang.ILookup
(valAt [this x] (str \"->\" x))
(valAt [this x y] y)))
[(:foo m) (:foo m 10)]"))))
(testing "IFn"
(is (= [:yo :three :six :twelve :eighteen :nineteen 19]
(bb nil "
(def m (reify clojure.lang.IFn
(invoke [this] :yo)
(invoke [this _ _ _] :three)
(invoke [this _ _ _ _ _ _] :six)
(invoke [this _ _ _ _ _ _ _ _ _ _ _ _] :twelve)
(invoke [this _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _] :eighteen)
(invoke [this _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _] :nineteen)
(applyTo [this args] (last args))))
[
(m)
(m 1 2 3)
(m 1 2 3 4 5 6)
(m 1 2 3 4 5 6 1 2 3 4 5 6)
(m 1 2 3 4 5 6 1 2 3 4 5 6 1 2 3 4 5 6)
(m 1 2 3 4 5 6 1 2 3 4 5 6 1 2 3 4 5 6 1)
(apply m (range 20))
]")))))
(deftest reify-object
(testing "empty methods"
(is (str/starts-with?
(bb nil "
(str (reify Object))")
"babashka.impl.reify")))
(testing "toString"
(is (= ":foo"
(bb nil "
(def m (reify Object
(toString [_] (str :foo))))
(str m)
"))))
(testing "toString + protocol"
(is (= ":dude1:dude2"
(bb nil "
(defprotocol Dude (dude [_])) (def obj (reify Object (toString [_] (str :dude1)) Dude (dude [_] :dude2))) (str (str obj) (dude obj))
"))))
(testing "Hashcode still works when only overriding toString"
(is (number?
(bb nil "
(def m (reify Object
(toString [_] (str :foo))))
(hash m)
")))))
(deftest reify-file-visitor-test
(let [prog '(do (ns dude
(:import
[java.io File]
[java.nio.file FileSystems FileVisitor FileVisitResult Files Path])
(:gen-class))
(defn match-paths
"Match glob to paths under root and return a collection of Path objects"
[^File root glob]
(let [root-path (.toPath root)
matcher (.getPathMatcher (FileSystems/getDefault) (str "glob:" glob))
paths (volatile! [])
visitor (reify FileVisitor
(visitFile [_ path attrs]
(when (.matches matcher (.relativize root-path ^Path path))
(vswap! paths conj path))
FileVisitResult/CONTINUE)
(visitFileFailed [_ path ex]
FileVisitResult/CONTINUE)
(preVisitDirectory [_ _ _] FileVisitResult/CONTINUE)
(postVisitDirectory [_ _ _] FileVisitResult/CONTINUE))]
(Files/walkFileTree root-path visitor)
@paths))
[(count (match-paths (File. ".") "**"))
;; visitFileFailed is implemented
(count (match-paths (File. "xxx") "**"))])
[x y] (bb nil prog)]
(is (pos? x))
(is (zero? y))))
(deftest reify-default-method-test
(let [prog '(do (def iter (let [coll [:a :b :c] idx (volatile! -1)]
(reify java.util.Iterator (hasNext [_] (< @idx 2))
(next [_] (nth coll (vswap! idx inc))))))
(def res (volatile! []))
(vswap! res conj (.hasNext iter))
(vswap! res conj (.next iter))
(.forEachRemaining
iter (reify java.util.function.Consumer (accept [_ x] (vswap! res conj x))))
(= [true :a :b :c] @res))]
(is (true? (bb nil prog)))))
(deftest reify-multiple-interfaces-test
(testing "throws exception"
(is (thrown?
clojure.lang.ExceptionInfo
(bb nil "
(reify
java.lang.Object (toString [_] \"foo\")
clojure.lang.Seqable (seq [_] '(1 2 3)))")))))
(deftest reify-runnable-and-garbage-collection-test
(is (bb nil "
(def cleaner (java.lang.ref.Cleaner/create))
(def deleted? (atom false))
(defn make-cleanable-ref []
(let [obj (Object.)]
(.register cleaner obj
(reify java.lang.Runnable
(run [_]
(reset! deleted? true))))
nil))
(defn force-gc []
(let [t (atom (Object.))
wr (java.lang.ref.WeakReference. @t)]
(reset! t nil)
(while (or (.get wr)
(not @deleted?))
(System/gc)
(System/runFinalization))))
(make-cleanable-ref)
(force-gc)
@deleted?
")))