Skip to content

Commit 38b35ea

Browse files
committed
Added basic unit test framework, started adding tests for stdlib
1 parent 5cf01f6 commit 38b35ea

File tree

12 files changed

+237
-13
lines changed

12 files changed

+237
-13
lines changed

Sources/SLisp/Repl.swift

-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@ public class Repl {
6565
do {
6666
let form = try Reader.read(input)
6767
let (lt, e) = try parser.eval(form, environment: environment)
68-
environment = e
6968
rv = lt
7069
} catch let LispError.runtime(msg:message) {
7170
return "Runtime Error: \(message)"

Sources/SLispCore/Errors.swift

+2-1
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,6 @@ public enum LispError: Error {
3636
}
3737

3838
public enum StandardErrors: String {
39-
case invalidArgumentException
39+
case invalidArgumentError
40+
case runtimeError
4041
}

Sources/SLispCore/Namespace.swift

+6-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,12 @@ extension Parser {
5454
func getValue(_ name: String, withEnvironment env: Environment) throws -> LispType {
5555
var targetNamespace: String?
5656
var binding: String
57-
57+
58+
// Special symbol that returns the current namespace
59+
if name == "*ns*" {
60+
return .symbol(env.namespace.name)
61+
}
62+
5863
// Split the input on the first forward slash to separate by
5964
let bindingComponents = name.characters.split(maxSplits: 1, omittingEmptySubsequences: false) {
6065
$0 == "/"

Sources/SLispCore/Parser.swift

+9-5
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ public class Parser {
230230
}
231231

232232
let (_, e) = try eval(doForm, environment: envs.last!)
233-
envs.append(e)
233+
//envs.append(e)
234234
}
235235

236236
// TCO
@@ -542,7 +542,7 @@ public class Parser {
542542
for (index, doForm) in tryBody!.enumerated() {
543543
let (form, _) = try eval(doForm, environment: env)
544544

545-
if index == 1 {
545+
if index == tryBody!.count - 1 {
546546
returnForm = (form, env)
547547
}
548548
}
@@ -555,8 +555,12 @@ public class Parser {
555555

556556
// Eval catch body
557557
if catchBody != nil {
558-
for catchForm in catchBody! {
559-
let (_, _) = try eval(catchForm, environment: env)
558+
for (index, catchForm) in catchBody!.enumerated() {
559+
let (form, _) = try eval(catchForm, environment: env)
560+
561+
if index == catchBody!.count - 1 {
562+
returnForm = (form, env)
563+
}
560564
}
561565
}
562566
} catch {
@@ -660,7 +664,7 @@ public class Parser {
660664

661665
for val in lispBody {
662666
let (lt, e) = try eval(val, environment: envs.last!)
663-
envs.append(e)
667+
//envs.append(e)
664668
mutableForm = lt
665669
}
666670
} else {

Sources/SLispCore/builtins/Core+CollectionOperations.swift

+44
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,50 @@ extension Core {
249249
}
250250

251251

252+
// MARK: take
253+
addBuiltin("take", docstring: """
254+
take
255+
(coll n)
256+
Returns the first n items of the collection
257+
""") { args, parser, env throws in
258+
try self.checkArgCount(funcName: "take", args: args, expectedNumArgs: 2)
259+
260+
guard case let .number(.integer(num)) = args[1] else {
261+
throw LispError.runtime(msg: "'take' expects the second argument to be an integer")
262+
}
263+
264+
if case let .list(list) = args[0] {
265+
return .list(Array(list.prefix(num)))
266+
} else if case let .string(str) = args[0] {
267+
return .string(String(str.prefix(num)))
268+
}
269+
270+
throw LispError.general(msg: "'take' expects an argument that is a list or a string")
271+
}
272+
273+
274+
// MARK: drop
275+
addBuiltin("drop", docstring: """
276+
drop
277+
(coll n)
278+
Drops the first n items of the collection
279+
""") { args, parser, env throws in
280+
try self.checkArgCount(funcName: "drop", args: args, expectedNumArgs: 2)
281+
282+
guard case let .number(.integer(num)) = args[1] else {
283+
throw LispError.runtime(msg: "'drop' expects the second argument to be an integer")
284+
}
285+
286+
if case let .list(list) = args[0] {
287+
return .list(Array(list.dropFirst(num)))
288+
} else if case let .string(str) = args[0] {
289+
return .string(String(str.dropFirst(num)))
290+
}
291+
292+
throw LispError.general(msg: "'drop' expects an argument that is a list or a string")
293+
}
294+
295+
252296
// MARK: at
253297
addBuiltin("at", docstring: """
254298
at

install.sh

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
INSTALL_ROOT=/usr/local/opt/slisp
44

55
swift build -c release -Xswiftc -static-stdlib
6-
pushd .build/release
6+
pushd .build/release > /dev/null
77
mkdir -p $INSTALL_ROOT
88
mkdir -p $INSTALL_ROOT/bin
99
cp SLisp $INSTALL_ROOT/bin
10-
popd
10+
popd > /dev/null
1111

1212
cp -R stdlib $INSTALL_ROOT

run.sh

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#!/bin/sh
22

3-
pushd ./.build/debug
3+
pushd ./.build/debug > /dev/null
44
./SLisp
5-
popd
5+
popd > /dev/null

runTests.sh

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/bin/sh
2+
3+
./install.sh
4+
pushd stdlibTests > /dev/null
5+
slisp test-suite.sl
6+
popd > /dev/null

stdlib/core/core.sl

+1-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@
6161
(while (< collidx (count coll))
6262
(let (item (at coll collidx))
6363
(if (f item)
64-
(set! processed (concat processed item))))
64+
(set! processed (concat processed (list item)))))
6565
(set! collidx (+ collidx 1)))
6666
processed))
6767

stdlib/test.sl

+64
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,67 @@
7474
(print (color-text :blue "Blue text"))
7575
(print (color-text :magenta "Magenta text"))
7676
(print (color-text :cyan "Cyan text")))
77+
78+
79+
;; Test function
80+
81+
(defmacro deftest
82+
#((name & body)
83+
(concat `(defn ~(symbol (str "test-" name))) body)))
84+
85+
86+
;; Test runner
87+
88+
(defn get-tests
89+
(ns-list)
90+
(filter #((x)
91+
(let (name (first x)
92+
val (second x))
93+
(&& (== (take (str name) 5) "test-") (function? val))))
94+
ns-list))
95+
96+
(defn run-test-file
97+
(path)
98+
(let (file-contents (read-string (str "(do " (slurp path) " *ns* )"))
99+
file-ns (eval file-contents)
100+
ns-contents (list-ns file-ns)
101+
tests (get-tests ns-contents))
102+
(print (str "Running test file " path))
103+
104+
; Run each test
105+
(let (test-results (map #((t) (run-test (second t) (drop (str (first t)) 5))) tests)
106+
test-count (count tests)
107+
pass-count (count (filter #((x) x) test-results))
108+
fail-count (count (filter #((x) (! x)) test-results)))
109+
110+
(print "Test file completed")
111+
(when pass-count
112+
(print (color-text :green (str "\t" pass-count " tests passed"))))
113+
(when fail-count
114+
(print (color-text :red (str "\t" fail-count " tests failed"))))
115+
116+
(== fail-count 0))))
117+
118+
(defn run-test-suite
119+
(& testFiles)
120+
(print "Running test suite")
121+
(let (results (map run-test-file testFiles)
122+
failed-test-files (filter #((x) (! x)) results))
123+
(if (> (count failed-test-files) 0)
124+
(do
125+
(print (color-text :red "Test suite failed"))
126+
(exit 1))
127+
(do
128+
(print (color-text :green "Test suite passed"))
129+
(exit 0)))))
130+
131+
(defn run-test
132+
(test test-name)
133+
(print (str "Running test: " test-name))
134+
(try
135+
(test)
136+
(print (color-text :green "\tPASS"))
137+
true
138+
(catch e
139+
(print (color-text :red "\tFAIL"))
140+
false)))

stdlibTests/core/core.sl

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
; MIT License
2+
;
3+
; Copyright (c) 2016 Andy Best
4+
;
5+
; Permission is hereby granted, free of charge, to any person obtaining a copy
6+
; of this software and associated documentation files (the "Software"), to deal
7+
; in the Software without restriction, including without limitation the rights
8+
; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
; copies of the Software, and to permit persons to whom the Software is
10+
; furnished to do so, subject to the following conditions:
11+
;
12+
; The above copyright notice and this permission notice shall be included in all
13+
; copies or substantial portions of the Software.
14+
;
15+
; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
; SOFTWARE.
22+
23+
(ns core-tests
24+
(:refer core.test))
25+
26+
(deftest map-applies-function-to-items ()
27+
(let (test-input '(1 2 3 4 5 6)
28+
add-fn #((x) (+ x 1)))
29+
30+
(assertEqual
31+
(map add-fn test-input)
32+
'(2 3 4 5 6 7))))
33+
34+
(deftest reduce-reduces-values ()
35+
(let (test-input '(1 2 3 4 5)
36+
add-fn #((x y) (+ x y)))
37+
38+
(assertEqual
39+
(reduce add-fn 0 test-input)
40+
15)))
41+
42+
(deftest filter-filters-values ()
43+
(let (test-input '(1 2 3 4 5 6 7 8 9 10)
44+
test-fn #((x) (== (mod x 2) 0)))
45+
46+
(assertEqual
47+
(filter test-fn test-input)
48+
'(2 4 6 8 10))))
49+
50+
(deftest second-returns-second-item ()
51+
(assertEqual
52+
(second '(1 2 3 4))
53+
2))
54+
55+
(deftest reverse-reverses-list ()
56+
(assertEqual
57+
(reverse '(1 2 3 4))
58+
'(4 3 2 1)))
59+
60+
(deftest reverse-reverses-string ()
61+
(assertEqual
62+
(reverse "Hello")
63+
"olleH"))
64+
65+
66+
(deftest less-than-or-equal ()
67+
(assert (<= 2 10))
68+
(assert (<= 2 2))
69+
(assert (! (<= 10 2))))
70+
71+
(deftest greater-than-or-equal ()
72+
(assert (>= 10 2))
73+
(assert (>= 10 10))
74+
(assert (! (>= 10 20))))

stdlibTests/test-suite.sl

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
; MIT License
2+
;
3+
; Copyright (c) 2016 Andy Best
4+
;
5+
; Permission is hereby granted, free of charge, to any person obtaining a copy
6+
; of this software and associated documentation files (the "Software"), to deal
7+
; in the Software without restriction, including without limitation the rights
8+
; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
; copies of the Software, and to permit persons to whom the Software is
10+
; furnished to do so, subject to the following conditions:
11+
;
12+
; The above copyright notice and this permission notice shall be included in all
13+
; copies or substantial portions of the Software.
14+
;
15+
; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
; SOFTWARE.
22+
23+
(ns stdlib-testsuite
24+
(:refer core.test))
25+
26+
(run-test-suite
27+
"core/core.sl")

0 commit comments

Comments
 (0)