mirror of
https://github.com/janet-lang/janet
synced 2024-11-06 00:36:17 +00:00
5de889419f
Shorten docstrings to be less like a tutorial. They get put into RAM and memory ain't free.
334 lines
13 KiB
Plaintext
334 lines
13 KiB
Plaintext
# Copyright (c) 2023 Calvin Rose & contributors
|
|
#
|
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
# of this software and associated documentation files (the "Software"), to
|
|
# deal in the Software without restriction, including without limitation the
|
|
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
# sell copies of the Software, and to permit persons to whom the Software is
|
|
# furnished to do so, subject to the following conditions:
|
|
#
|
|
# The above copyright notice and this permission notice shall be included in
|
|
# all copies or substantial portions of the Software.
|
|
#
|
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
# IN THE SOFTWARE.
|
|
|
|
(import ./helper :prefix "" :exit true)
|
|
(start-suite 10)
|
|
|
|
# index-of
|
|
(assert (= nil (index-of 10 [])) "index-of 1")
|
|
(assert (= nil (index-of 10 [1 2 3])) "index-of 2")
|
|
(assert (= 1 (index-of 2 [1 2 3])) "index-of 3")
|
|
(assert (= 0 (index-of :a [:a :b :c])) "index-of 4")
|
|
(assert (= nil (index-of :a {})) "index-of 5")
|
|
(assert (= :a (index-of :A {:a :A :b :B})) "index-of 6")
|
|
(assert (= :a (index-of :A @{:a :A :b :B})) "index-of 7")
|
|
(assert (= 0 (index-of (chr "a") "abc")) "index-of 8")
|
|
(assert (= nil (index-of (chr "a") "")) "index-of 9")
|
|
(assert (= nil (index-of 10 @[])) "index-of 10")
|
|
(assert (= nil (index-of 10 @[1 2 3])) "index-of 11")
|
|
# NOTE: These is a motivation for the has-value? and has-key? functions below
|
|
|
|
# returns false despite key present
|
|
(assert (= false (index-of 8 {true 7 false 8})) "index-of corner key (false) 1")
|
|
(assert (= false (index-of 8 @{false 8})) "index-of corner key (false) 2")
|
|
# still returns null
|
|
(assert (= nil (index-of 7 {false 8})) "index-of corner key (false) 3")
|
|
|
|
# has-value?
|
|
(assert (= false (has-value? [] "foo")) "has-value? 1")
|
|
(assert (= true (has-value? [4 7 1 3] 4)) "has-value? 2")
|
|
(assert (= false (has-value? [4 7 1 3] 22)) "has-value? 3")
|
|
(assert (= false (has-value? @[1 2 3] 4)) "has-value? 4")
|
|
(assert (= true (has-value? @[:a :b :c] :a)) "has-value? 5")
|
|
(assert (= false (has-value? {} :foo)) "has-value? 6")
|
|
(assert (= true (has-value? {:a :A :b :B} :A)) "has-value? 7")
|
|
(assert (= true (has-value? {:a :A :b :B} :A)) "has-value? 7")
|
|
(assert (= true (has-value? @{:a :A :b :B} :A)) "has-value? 8")
|
|
(assert (= true (has-value? "abc" (chr "a"))) "has-value? 9")
|
|
(assert (= false (has-value? "abc" "1")) "has-value? 10")
|
|
# weird true/false corner cases, should align with "index-of corner key {k}" cases
|
|
(assert (= true (has-value? {true 7 false 8} 8)) "has-value? corner key (false) 1")
|
|
(assert (= true (has-value? @{false 8} 8)) "has-value? corner key (false) 2")
|
|
(assert (= false (has-value? {false 8} 7)) "has-value? corner key (false) 3")
|
|
|
|
# has-key?
|
|
(do
|
|
(var test-has-key-auto 0)
|
|
(defn test-has-key [col key expected &keys {:name name}]
|
|
``Test that has-key has the outcome `expected`, and that if
|
|
the result is true, then ensure (in key) does not fail either``
|
|
(assert (boolean? expected))
|
|
(default name (string "has-key? " (++ test-has-key-auto)))
|
|
(assert (= expected (has-key? col key)) name)
|
|
(if
|
|
# guarenteed by `has-key?` to never fail
|
|
expected (in col key)
|
|
# if `has-key?` is false, then `in` should fail (for indexed types)
|
|
#
|
|
# For dictionary types, it should return nil
|
|
(let [[success retval] (protect (in col key))]
|
|
(def should-succeed (dictionary? col))
|
|
(assert
|
|
(= success should-succeed)
|
|
(string/format
|
|
"%s: expected (in col key) to %s, but got %q"
|
|
name (if expected "succeed" "fail") retval)))))
|
|
|
|
(test-has-key [] 0 false) # 1
|
|
(test-has-key [4 7 1 3] 2 true) # 2
|
|
(test-has-key [4 7 1 3] 22 false) # 3
|
|
(test-has-key @[1 2 3] 4 false) # 4
|
|
(test-has-key @[:a :b :c] 2 true) # 5
|
|
(test-has-key {} :foo false) # 6
|
|
(test-has-key {:a :A :b :B} :a true) # 7
|
|
(test-has-key {:a :A :b :B} :A false) # 8
|
|
(test-has-key @{:a :A :b :B} :a true) # 9
|
|
(test-has-key "abc" 1 true) # 10
|
|
(test-has-key "abc" 4 false) # 11
|
|
# weird true/false corner cases
|
|
#
|
|
# Tries to mimic the corresponding corner cases in has-value? and index-of,
|
|
# but with keys/values inverted
|
|
#
|
|
# in the first two cases (truthy? (get val col)) would have given false negatives
|
|
(test-has-key {7 true 8 false} 8 true :name "has-key? corner value (false) 1")
|
|
(test-has-key @{8 false} 8 true :name "has-key? corner value (false) 2")
|
|
(test-has-key @{8 false} 7 false :name "has-key? corner value (false) 3"))
|
|
|
|
# Regression
|
|
(assert (= {:x 10} (|(let [x $] ~{:x ,x}) 10)) "issue 463")
|
|
|
|
# macex testing
|
|
(assert (deep= (macex1 '~{1 2 3 4}) '~{1 2 3 4}) "macex1 qq struct")
|
|
(assert (deep= (macex1 '~@{1 2 3 4}) '~@{1 2 3 4}) "macex1 qq table")
|
|
(assert (deep= (macex1 '~(1 2 3 4)) '~[1 2 3 4]) "macex1 qq tuple")
|
|
(assert (= :brackets (tuple/type (1 (macex1 '~[1 2 3 4])))) "macex1 qq bracket tuple")
|
|
(assert (deep= (macex1 '~@[1 2 3 4 ,blah]) '~@[1 2 3 4 ,blah]) "macex1 qq array")
|
|
|
|
# Sourcemaps in threading macros
|
|
(defn check-threading [macro expansion]
|
|
(def expanded (macex1 (tuple macro 0 '(x) '(y))))
|
|
(assert (= expanded expansion) (string macro " expansion value"))
|
|
(def smap-x (tuple/sourcemap (get expanded 1)))
|
|
(def smap-y (tuple/sourcemap expanded))
|
|
(def line first)
|
|
(defn column [t] (t 1))
|
|
(assert (not= smap-x [-1 -1]) (string macro " x sourcemap existence"))
|
|
(assert (not= smap-y [-1 -1]) (string macro " y sourcemap existence"))
|
|
(assert (or (< (line smap-x) (line smap-y))
|
|
(and (= (line smap-x) (line smap-y))
|
|
(< (column smap-x) (column smap-y))))
|
|
(string macro " relation between x and y sourcemap")))
|
|
|
|
(check-threading '-> '(y (x 0)))
|
|
(check-threading '->> '(y (x 0)))
|
|
|
|
# keep-syntax
|
|
(let [brak '[1 2 3]
|
|
par '(1 2 3)]
|
|
|
|
(tuple/setmap brak 2 1)
|
|
|
|
(assert (deep= (keep-syntax brak @[1 2 3]) @[1 2 3]) "keep-syntax brackets ignore array")
|
|
(assert (= (keep-syntax! brak @[1 2 3]) '[1 2 3]) "keep-syntax! brackets replace array")
|
|
|
|
(assert (= (keep-syntax! par (map inc @[1 2 3])) '(2 3 4)) "keep-syntax! parens coerce array")
|
|
(assert (not= (keep-syntax! brak @[1 2 3]) '(1 2 3)) "keep-syntax! brackets not parens")
|
|
(assert (not= (keep-syntax! par @[1 2 3]) '[1 2 3]) "keep-syntax! parens not brackets")
|
|
(assert (= (tuple/sourcemap brak)
|
|
(tuple/sourcemap (keep-syntax! brak @[1 2 3]))) "keep-syntax! brackets source map")
|
|
|
|
(keep-syntax par brak)
|
|
(assert (not= (tuple/sourcemap brak) (tuple/sourcemap par)) "keep-syntax no mutate")
|
|
(assert (= (keep-syntax 1 brak) brak) "keep-syntax brackets ignore type"))
|
|
|
|
# Cancel test
|
|
(def f (fiber/new (fn [&] (yield 1) (yield 2) (yield 3) 4) :yti))
|
|
(assert (= 1 (resume f)) "cancel resume 1")
|
|
(assert (= 2 (resume f)) "cancel resume 2")
|
|
(assert (= :hi (cancel f :hi)) "cancel resume 3")
|
|
(assert (= :error (fiber/status f)) "cancel resume 4")
|
|
|
|
# Curenv
|
|
(assert (= (curenv) (curenv 0)) "curenv 1")
|
|
(assert (= (table/getproto (curenv)) (curenv 1)) "curenv 2")
|
|
(assert (= nil (curenv 1000000)) "curenv 3")
|
|
(assert (= root-env (curenv 1)) "curenv 4")
|
|
|
|
# Import macro test
|
|
(assert-no-error "import macro 1" (macex '(import a :as b :fresh maybe)))
|
|
(assert (deep= ~(,import* "a" :as "b" :fresh maybe) (macex '(import a :as b :fresh maybe))) "import macro 2")
|
|
|
|
# #477 walk preserving bracket type
|
|
(assert (= :brackets (tuple/type (postwalk identity '[]))) "walk square brackets 1")
|
|
(assert (= :brackets (tuple/type (walk identity '[]))) "walk square brackets 2")
|
|
|
|
# # off by 1 error in inttypes
|
|
(assert (= (int/s64 "-0x8000_0000_0000_0000") (+ (int/s64 "0x7FFF_FFFF_FFFF_FFFF") 1)) "int types wrap around")
|
|
|
|
#
|
|
# Longstring indentation
|
|
#
|
|
|
|
(defn reindent
|
|
"Reindent a the contents of a longstring as the Janet parser would.
|
|
This include removing leading and trailing newlines."
|
|
[text indent]
|
|
|
|
# Detect minimum indent
|
|
(var rewrite true)
|
|
(each index (string/find-all "\n" text)
|
|
(for i (+ index 1) (+ index indent 1)
|
|
(case (get text i)
|
|
nil (break)
|
|
(chr "\n") (break)
|
|
(chr " ") nil
|
|
(set rewrite false))))
|
|
|
|
# Only re-indent if no dedented characters.
|
|
(def str
|
|
(if rewrite
|
|
(peg/replace-all ~(* "\n" (between 0 ,indent " ")) "\n" text)
|
|
text))
|
|
|
|
(def first-nl (= (chr "\n") (first str)))
|
|
(def last-nl (= (chr "\n") (last str)))
|
|
(string/slice str (if first-nl 1 0) (if last-nl -2)))
|
|
|
|
(defn reindent-reference
|
|
"Same as reindent but use parser functionality. Useful for validating conformance."
|
|
[text indent]
|
|
(if (empty? text) (break text))
|
|
(def source-code
|
|
(string (string/repeat " " indent) "``````"
|
|
text
|
|
"``````"))
|
|
(parse source-code))
|
|
|
|
(var indent-counter 0)
|
|
(defn check-indent
|
|
[text indent]
|
|
(++ indent-counter)
|
|
(let [a (reindent text indent)
|
|
b (reindent-reference text indent)]
|
|
(assert (= a b) (string "indent " indent-counter " (indent=" indent ")"))))
|
|
|
|
(check-indent "" 0)
|
|
(check-indent "\n" 0)
|
|
(check-indent "\n" 1)
|
|
(check-indent "\n\n" 0)
|
|
(check-indent "\n\n" 1)
|
|
(check-indent "\nHello, world!" 0)
|
|
(check-indent "\nHello, world!" 1)
|
|
(check-indent "Hello, world!" 0)
|
|
(check-indent "Hello, world!" 1)
|
|
(check-indent "\n Hello, world!" 4)
|
|
(check-indent "\n Hello, world!\n" 4)
|
|
(check-indent "\n Hello, world!\n " 4)
|
|
(check-indent "\n Hello, world!\n " 4)
|
|
(check-indent "\n Hello, world!\n dedented text\n " 4)
|
|
(check-indent "\n Hello, world!\n indented text\n " 4)
|
|
|
|
# String bugs
|
|
(assert (deep= (string/find-all "qq" "qqq") @[0 1]) "string/find-all 1")
|
|
(assert (deep= (string/find-all "q" "qqq") @[0 1 2]) "string/find-all 2")
|
|
(assert (deep= (string/split "qq" "1qqqqz") @["1" "" "z"]) "string/split 1")
|
|
(assert (deep= (string/split "aa" "aaa") @["" "a"]) "string/split 2")
|
|
|
|
# Comparisons
|
|
(assert (> 1e23 100) "less than immediate 1")
|
|
(assert (> 1e23 1000) "less than immediate 2")
|
|
(assert (< 100 1e23) "greater than immediate 1")
|
|
(assert (< 1000 1e23) "greater than immediate 2")
|
|
|
|
# os/execute with environment variables
|
|
(assert (= 0 (os/execute [(dyn :executable) "-e" "(+ 1 2 3)"] :pe (merge (os/environ) {"HELLO" "WORLD"}))) "os/execute with env")
|
|
|
|
# Regression #638
|
|
(compwhen
|
|
(dyn 'ev/go)
|
|
(assert
|
|
(= [true :caught]
|
|
(protect
|
|
(try
|
|
(do
|
|
(ev/sleep 0)
|
|
(with-dyns []
|
|
(ev/sleep 0)
|
|
(error "oops")))
|
|
([err] :caught))))
|
|
"regression #638"))
|
|
|
|
|
|
# Struct prototypes
|
|
(def x (struct/with-proto {1 2 3 4} 5 6))
|
|
(def y (-> x marshal unmarshal))
|
|
(def z {1 2 3 4})
|
|
(assert (= 2 (get x 1)) "struct get proto value 1")
|
|
(assert (= 4 (get x 3)) "struct get proto value 2")
|
|
(assert (= 6 (get x 5)) "struct get proto value 3")
|
|
(assert (= x y) "struct proto marshal equality 1")
|
|
(assert (= (getproto x) (getproto y)) "struct proto marshal equality 2")
|
|
(assert (= 0 (cmp x y)) "struct proto comparison 1")
|
|
(assert (= 0 (cmp (getproto x) (getproto y))) "struct proto comparison 2")
|
|
(assert (not= (cmp x z) 0) "struct proto comparison 3")
|
|
(assert (not= (cmp y z) 0) "struct proto comparison 4")
|
|
(assert (not= x z) "struct proto comparison 5")
|
|
(assert (not= y z) "struct proto comparison 6")
|
|
(assert (= (x 5) 6) "struct proto get 1")
|
|
(assert (= (y 5) 6) "struct proto get 1")
|
|
(assert (deep= x y) "struct proto deep= 1")
|
|
(assert (deep-not= x z) "struct proto deep= 2")
|
|
(assert (deep-not= y z) "struct proto deep= 3")
|
|
|
|
# Issue #751
|
|
(def t {:side false})
|
|
(assert (nil? (get-in t [:side :note])) "get-in with false value")
|
|
(assert (= (get-in t [:side :note] "dflt") "dflt")
|
|
"get-in with false value and default")
|
|
|
|
(assert (= (math/gcd 462 1071) 21) "math/gcd 1")
|
|
(assert (= (math/lcm 462 1071) 23562) "math/lcm 1")
|
|
|
|
# Evaluate stream with `dofile`
|
|
(def [r w] (os/pipe))
|
|
(:write w "(setdyn :x 10)")
|
|
(:close w)
|
|
(def stream-env (dofile r))
|
|
(assert (= (stream-env :x) 10) "dofile stream 1")
|
|
|
|
# Issue #861 - should be valgrind clean
|
|
(def step1 "(a b c d)\n")
|
|
(def step2 "(a b)\n")
|
|
(def p1 (parser/new))
|
|
(parser/state p1)
|
|
(parser/consume p1 step1)
|
|
(loop [v :iterate (parser/produce p1)])
|
|
(parser/state p1)
|
|
(def p2 (parser/clone p1))
|
|
(parser/state p2)
|
|
(parser/consume p2 step2)
|
|
(loop [v :iterate (parser/produce p2)])
|
|
(parser/state p2)
|
|
|
|
# Check missing struct proto bug.
|
|
(assert (struct/getproto (struct/with-proto {:a 1} :b 2 :c nil)) "missing struct proto")
|
|
|
|
# Test thaw and freeze
|
|
(def table-to-freeze @{:c 22 :b [1 2 3 4] :d @"test" :e "test2"})
|
|
(def table-to-freeze-with-inline-proto @{:a @[1 2 3] :b @[1 2 3 4] :c 22 :d @"test" :e @"test2"})
|
|
(def struct-to-thaw (struct/with-proto {:a [1 2 3]} :c 22 :b [1 2 3 4] :d "test" :e "test2"))
|
|
(table/setproto table-to-freeze @{:a @[1 2 3]})
|
|
(assert (deep= {:a [1 2 3] :b [1 2 3 4] :c 22 :d "test" :e "test2"} (freeze table-to-freeze)))
|
|
(assert (deep= table-to-freeze-with-inline-proto (thaw table-to-freeze)))
|
|
(assert (deep= table-to-freeze-with-inline-proto (thaw struct-to-thaw)))
|
|
|
|
(end-suite)
|