summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar major seitan <majorseitan@users.noreply.github.com>2018-06-02 14:58:45 -0400
committerGravatar GitHub <noreply@github.com>2018-06-02 14:58:45 -0400
commit87c86c5d2066a8805c7e72e3279ae8d5822f0c90 (patch)
tree02fa912c9b44ee07c0fcfe2bad0e2b686d0103b3
parentb4ffc6c4837e06c48a1a15ea662c25aa92566bf6 (diff)
parent1c493e9ec47f4754dd7237078e8c4f3300925ce3 (diff)
Merge branch 'master' into master
-rw-r--r--.gitignore5
-rw-r--r--include/urweb/urweb_cpp.h2
-rw-r--r--lib/ur/basis.urs5
-rw-r--r--src/c/urweb.c5
-rw-r--r--src/cjr_print.sml5
-rw-r--r--src/elaborate.sml3
-rw-r--r--tests/DynChannel.py20
-rw-r--r--tests/Makefile26
-rw-r--r--tests/aborter.py11
-rw-r--r--tests/aborter.urp1
-rw-r--r--tests/aborter2.py11
-rw-r--r--tests/active.py14
-rw-r--r--tests/activeBlock.py20
-rw-r--r--tests/activeBlock.ur2
-rw-r--r--tests/activeEmpty.py12
-rw-r--r--tests/activeFocus.py18
-rw-r--r--tests/activeFocus.ur2
-rw-r--r--tests/agg.py8
-rw-r--r--tests/agg.ur20
-rw-r--r--tests/ahead.py15
-rw-r--r--tests/alert.py11
-rw-r--r--tests/alert.ur2
-rw-r--r--tests/alert.urp3
-rw-r--r--tests/align.py11
-rw-r--r--tests/appjs.py11
-rw-r--r--tests/appjs.ur2
-rw-r--r--tests/ascdesc.py11
-rw-r--r--tests/ascdesc.ur14
-rw-r--r--tests/ascdesc.urp3
-rw-r--r--tests/attrMangle.py11
-rw-r--r--tests/attrs_escape.py10
-rw-r--r--tests/attrs_escape.ur10
-rw-r--r--tests/autocomp.py15
-rw-r--r--tests/autocomp.ur8
-rw-r--r--tests/babySpawn.py12
-rw-r--r--tests/base.py29
-rw-r--r--tests/bindpat.py9
-rw-r--r--tests/bindpat.ur7
-rwxr-xr-xtests/driver.sh25
-rw-r--r--tests/entities.py14
-rw-r--r--tests/entities.ur6
-rw-r--r--tests/fact.py10
-rw-r--r--tests/filter.py9
-rw-r--r--tests/filter.ur17
-rw-r--r--tests/jsbspace.py11
-rw-r--r--tests/jsbspace.ur12
-rw-r--r--tests/jsonTest.py16
-rw-r--r--tests/listGroupBy.ur13
-rw-r--r--tests/listGroupBy.urp4
-rw-r--r--tests/pairUnify.ur6
-rw-r--r--tests/unurlify2.ur16
51 files changed, 494 insertions, 49 deletions
diff --git a/.gitignore b/.gitignore
index b30fa842..377a9e5d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -76,3 +76,8 @@ libtool
include/urweb/config.h
include/urweb/config.h.in
include/urweb/stamp-h1
+
+# python files
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
diff --git a/include/urweb/urweb_cpp.h b/include/urweb/urweb_cpp.h
index 0d5f5e0e..2c60a781 100644
--- a/include/urweb/urweb_cpp.h
+++ b/include/urweb/urweb_cpp.h
@@ -358,8 +358,6 @@ uw_Basis_string uw_Basis_timef(struct uw_context *, const char *fmt, uw_Basis_ti
uw_Basis_time uw_Basis_stringToTimef(struct uw_context *, const char *fmt, uw_Basis_string);
uw_Basis_time uw_Basis_stringToTimef_error(struct uw_context *, const char *fmt, uw_Basis_string);
-uw_Basis_string uw_Basis_crypt(struct uw_context *, uw_Basis_string key, uw_Basis_string salt);
-
uw_Basis_bool uw_Basis_eq_time(struct uw_context *, uw_Basis_time, uw_Basis_time);
uw_Basis_bool uw_Basis_lt_time(struct uw_context *, uw_Basis_time, uw_Basis_time);
uw_Basis_bool uw_Basis_le_time(struct uw_context *, uw_Basis_time, uw_Basis_time);
diff --git a/lib/ur/basis.urs b/lib/ur/basis.urs
index c354d784..dc1b9b76 100644
--- a/lib/ur/basis.urs
+++ b/lib/ur/basis.urs
@@ -192,11 +192,6 @@ val datetimeSecond : time -> int
val datetimeDayOfWeek : time -> int
-(** * Encryption *)
-
-val crypt : string -> string -> string
-
-
(** HTTP operations *)
con http_cookie :: Type -> Type
diff --git a/src/c/urweb.c b/src/c/urweb.c
index 504597ef..283efcdd 100644
--- a/src/c/urweb.c
+++ b/src/c/urweb.c
@@ -4490,11 +4490,6 @@ failure_kind uw_runCallback(uw_context ctx, void (*callback)(uw_context)) {
return r;
}
-uw_Basis_string uw_Basis_crypt(uw_context ctx, uw_Basis_string key, uw_Basis_string salt) {
- char buf[14];
- return uw_strdup(ctx, DES_fcrypt(key, salt, buf));
-}
-
uw_Basis_bool uw_Basis_eq_time(uw_context ctx, uw_Basis_time t1, uw_Basis_time t2) {
(void)ctx;
return !!(t1.seconds == t2.seconds && t1.microseconds == t2.microseconds);
diff --git a/src/cjr_print.sml b/src/cjr_print.sml
index 1fdb45d9..43265fb8 100644
--- a/src/cjr_print.sml
+++ b/src/cjr_print.sml
@@ -659,7 +659,10 @@ fun unurlify fromClient env (t, loc) =
doEm rest,
string ")"]
in
- doEm xncs
+ box [string "(request[0] == '/' ? ++request : request,",
+ newline,
+ doEm xncs,
+ string ")"]
end
| TDatatype (Option, i, xncs) =>
diff --git a/src/elaborate.sml b/src/elaborate.sml
index 4a04d4bf..51d00bd8 100644
--- a/src/elaborate.sml
+++ b/src/elaborate.sml
@@ -4046,7 +4046,8 @@ and elabDecl (dAll as (d, loc), (env, denv, gs)) =
| L.PAnnot (p', _) => singleVar p'
| _ => NONE
in
- unifyCons env loc et pt;
+ (unifyCons env loc et pt
+ handle CUnify (c1, c2, env', err) => expError env (Unify (e', c1, c2, env', err)));
(case exhaustive (env, et, [p'], loc) of
NONE => ()
diff --git a/tests/DynChannel.py b/tests/DynChannel.py
new file mode 100644
index 00000000..7af5ea78
--- /dev/null
+++ b/tests/DynChannel.py
@@ -0,0 +1,20 @@
+import unittest
+import base
+
+class Suite(base.Base):
+ def test_1(self):
+ """Test case 1"""
+ self.start('DynChannel/main')
+
+ # initial state: only Register is visible
+ reg = self.xpath('button')
+ reg.click()
+ # and we get two another state: either Register or Send visible
+ send = self.xpath('span/button')
+ send.click()
+ alert = self.driver.switch_to.alert
+ self.assertEqual("Got something from the channel", alert.text)
+ alert.accept()
+ # we got the message back
+ span = self.xpath('span/span')
+ self.assertEqual("blabla", span.text)
diff --git a/tests/Makefile b/tests/Makefile
index 5313d12d..ecf5557b 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -2,3 +2,29 @@ all: test.o
test.o: test.c
gcc -c test.c -o test.o
+###
+
+simple::
+ ./driver.sh aborter2
+ ./driver.sh aborter
+ ./driver.sh activeBlock
+ ./driver.sh activeFocus
+ ./driver.sh active
+ ./driver.sh agg
+ ./driver.sh ahead
+ ./driver.sh alert
+ ./driver.sh align
+ ./driver.sh appjs
+ ./driver.sh ascdesc
+ echo ./driver.sh attrMangle
+ ./driver.sh attrs_escape
+ echo ./driver.sh attrs
+ ./driver.sh autocomp
+ ./driver.sh babySpawn
+ ./driver.sh bindpat
+ ./driver.sh DynChannel
+ ./driver.sh jsonTest
+ ./driver.sh entities
+ ./driver.sh fact
+ ./driver.sh filter
+ ./driver.sh jsbspace
diff --git a/tests/aborter.py b/tests/aborter.py
new file mode 100644
index 00000000..8379c656
--- /dev/null
+++ b/tests/aborter.py
@@ -0,0 +1,11 @@
+import unittest
+import base
+
+class Suite(base.Base):
+ def test_1(self):
+ """Test case 1"""
+ self.start('Aborter/main')
+ self.assertEqual("Fatal Error", self.driver.title)
+ txt = self.body_text()
+ self.assertEqual("Fatal error: :0:0-0:0: No way, Jose!", txt)
+
diff --git a/tests/aborter.urp b/tests/aborter.urp
index fc1925ae..8c971440 100644
--- a/tests/aborter.urp
+++ b/tests/aborter.urp
@@ -1,4 +1,5 @@
database dbname=aborter
sql aborter.sql
+safeGet Aborter/main
aborter
diff --git a/tests/aborter2.py b/tests/aborter2.py
new file mode 100644
index 00000000..c3f1e10e
--- /dev/null
+++ b/tests/aborter2.py
@@ -0,0 +1,11 @@
+import unittest
+import base
+
+class Suite(base.Base):
+ def test_1(self):
+ """Test case 1"""
+ self.start('Aborter2/main')
+ self.assertEqual("", self.driver.title)
+ txt = self.body_text()
+ self.assertEqual("Result: 0", txt)
+
diff --git a/tests/active.py b/tests/active.py
new file mode 100644
index 00000000..08846ac5
--- /dev/null
+++ b/tests/active.py
@@ -0,0 +1,14 @@
+import unittest
+import base
+
+class Suite(base.Base):
+ def test_1(self):
+ """Test case 1"""
+ self.start()
+ b1 = self.xpath('span[1]/button')
+ b2 = self.xpath('span[2]/button')
+ for _ in range(3):
+ b1.click()
+ for _ in range(5):
+ b2.click()
+ self.assertEqual("3\n5", self.body_text())
diff --git a/tests/activeBlock.py b/tests/activeBlock.py
new file mode 100644
index 00000000..d0e43fdb
--- /dev/null
+++ b/tests/activeBlock.py
@@ -0,0 +1,20 @@
+import unittest
+import base
+import time
+
+class Suite(base.Base):
+ def test_1(self):
+ """Test case 1"""
+ self.start()
+ alert = self.driver.switch_to.alert
+ self.assertEqual("Error: May not 'sleep' in main thread of 'code' for <active>", alert.text)
+ alert.accept()
+ time.sleep(0.1)
+ alert = self.driver.switch_to.alert
+ self.assertEqual("Hi!", alert.text)
+ alert.accept()
+ button = self.xpath('span[1]/button')
+ button.click()
+ txt = self.body_text()
+ self.assertEqual("Hi! Click me! Success", txt)
+
diff --git a/tests/activeBlock.ur b/tests/activeBlock.ur
index 5560edda..bced4af3 100644
--- a/tests/activeBlock.ur
+++ b/tests/activeBlock.ur
@@ -1,7 +1,7 @@
fun main () : transaction page = return <xml><body>
<active code={s <- source ""; return <xml>
<dyn signal={s <- signal s; return (txt s)}/>
- <button onclick={fn _ => set s "Hi!"}/>
+ <button onclick={fn _ => set s "Hi!"}>Click me!</button>
</xml>}/>
<active code={sleep 1; return <xml>Hi!</xml>}/>
diff --git a/tests/activeEmpty.py b/tests/activeEmpty.py
new file mode 100644
index 00000000..8872833a
--- /dev/null
+++ b/tests/activeEmpty.py
@@ -0,0 +1,12 @@
+import unittest
+import base
+
+class Suite(base.Base):
+ def test_1(self):
+ """Test case 1"""
+ self.start()
+ alert = self.driver.switch_to.alert
+ self.assertEqual("Howdy, neighbor!", alert.text)
+ alert.accept()
+ txt = self.body_text()
+ self.assertEqual("This one ain't empty.", txt)
diff --git a/tests/activeFocus.py b/tests/activeFocus.py
new file mode 100644
index 00000000..47b9a921
--- /dev/null
+++ b/tests/activeFocus.py
@@ -0,0 +1,18 @@
+import unittest
+import base
+
+class Suite(base.Base):
+ def test_1(self):
+ """Test case 1"""
+ self.start()
+ uw0 = self.xpath('input[2]')
+ active = self.driver.switch_to.active_element
+ self.assertEqual(uw0, active)
+ def test_2(self):
+ """Test case 2"""
+ self.start('dynamic')
+ btn = self.xpath('button')
+ btn.click()
+ uw1 = self.xpath('span/input[2]')
+ active = self.driver.switch_to.active_element
+ self.assertEqual(uw1, active)
diff --git a/tests/activeFocus.ur b/tests/activeFocus.ur
index 94d465e9..82d2c0c9 100644
--- a/tests/activeFocus.ur
+++ b/tests/activeFocus.ur
@@ -14,5 +14,5 @@ fun dynamic () : transaction page =
<ctextbox/>
<ctextbox id={i}/>
<active code={giveFocus i; return <xml>Done</xml>}/>
- </xml>}/>
+ </xml>}>Click</button>
</body></xml>
diff --git a/tests/agg.py b/tests/agg.py
new file mode 100644
index 00000000..0b421d37
--- /dev/null
+++ b/tests/agg.py
@@ -0,0 +1,8 @@
+import unittest
+import base
+
+class Suite(base.Base):
+ def test_1(self):
+ """Test case 1"""
+ self.start('Agg/main')
+ self.assertEqual("0;1;2;\na, 50;", self.body_text())
diff --git a/tests/agg.ur b/tests/agg.ur
index 19a8644b..2d8eed43 100644
--- a/tests/agg.ur
+++ b/tests/agg.ur
@@ -1,13 +1,23 @@
table t1 : {A : int, B : string, C : float}
table t2 : {A : float, D : int, E : option string}
-val q1 : sql_query [] _ _ = (SELECT COUNT( * ) FROM t1)
-val q2 : sql_query [] _ _ = (SELECT AVG(t1.A) FROM t1)
-val q3 : sql_query [] _ _ = (SELECT SUM(t1.C) FROM t1)
-val q4 : sql_query [] _ _ = (SELECT MIN(t1.B), MAX(t1.A) FROM t1)
-val q5 : sql_query [] _ _ = (SELECT SUM(t1.A) FROM t1 GROUP BY t1.B)
+val q1 : sql_query [] [] _ _ = (SELECT COUNT( * ) FROM t1)
+val q2 : sql_query [] [] _ _ = (SELECT AVG(t1.A) FROM t1)
+val q3 : sql_query [] [] _ _ = (SELECT SUM(t1.C) FROM t1)
+val q4 : sql_query [] [] _ _ = (SELECT MIN(t1.B), MAX(t1.A) FROM t1)
+val q5 : sql_query [] [] _ _ = (SELECT SUM(t1.A) FROM t1 GROUP BY t1.B)
val q6 = (SELECT COUNT(t2.E) FROM t2 GROUP BY t2.D)
+task initialize = fn () =>
+ dml (INSERT INTO t1 (A, B, C) VALUES (1, 'a', 1.0));
+ dml (INSERT INTO t1 (A, B, C) VALUES (2, 'b', 2.0));
+ dml (INSERT INTO t1 (A, B, C) VALUES (50, 'c', 99.0));
+ dml (INSERT INTO t2 (A, D, E) VALUES (1.0, 1, NULL));
+ dml (INSERT INTO t2 (A, D, E) VALUES (1.0, 2, {[Some "a"]}));
+ dml (INSERT INTO t2 (A, D, E) VALUES (1.0, 3, NULL));
+ dml (INSERT INTO t2 (A, D, E) VALUES (1.0, 3, {[Some "b"]}));
+ dml (INSERT INTO t2 (A, D, E) VALUES (1.0, 3, {[Some "c"]}))
+
fun main () : transaction page =
xml <- queryX q6 (fn r => <xml>{[r.1]};</xml>);
xml2 <- queryX q4 (fn r => <xml>{[r.1]}, {[r.2]};</xml>);
diff --git a/tests/ahead.py b/tests/ahead.py
new file mode 100644
index 00000000..6e767948
--- /dev/null
+++ b/tests/ahead.py
@@ -0,0 +1,15 @@
+import unittest
+import base
+import time
+
+class Suite(base.Base):
+ def test_1(self):
+ """Test case 1"""
+ self.start()
+ alert = self.driver.switch_to.alert
+ self.assertEqual("Hi!", alert.text)
+ alert.accept()
+ time.sleep(0.1)
+ alert = self.driver.switch_to.alert
+ self.assertEqual("Bye!", alert.text)
+ alert.accept()
diff --git a/tests/alert.py b/tests/alert.py
new file mode 100644
index 00000000..4b783d50
--- /dev/null
+++ b/tests/alert.py
@@ -0,0 +1,11 @@
+import unittest
+import base
+
+class Suite(base.Base):
+ def test_1(self):
+ """Test case 1"""
+ self.start()
+ el = self.xpath('a')
+ el.click()
+ alert = self.driver.switch_to.alert
+ self.assertEqual("You clicked it! That's some fancy shooting!", alert.text)
diff --git a/tests/alert.ur b/tests/alert.ur
index 3fe68d75..7a290921 100644
--- a/tests/alert.ur
+++ b/tests/alert.ur
@@ -1,3 +1,3 @@
fun main () : transaction page = return <xml><body>
- <a onclick={alert "You clicked it! That's some fancy shooting!"}>Click Me!</a>
+ <a onclick={fn _ => alert "You clicked it! That's some fancy shooting!"}>Click Me!</a>
</body></xml>
diff --git a/tests/alert.urp b/tests/alert.urp
deleted file mode 100644
index 3976e9b0..00000000
--- a/tests/alert.urp
+++ /dev/null
@@ -1,3 +0,0 @@
-debug
-
-alert
diff --git a/tests/align.py b/tests/align.py
new file mode 100644
index 00000000..525ab4e6
--- /dev/null
+++ b/tests/align.py
@@ -0,0 +1,11 @@
+import unittest
+import base
+
+class Suite(base.Base):
+ def test_1(self):
+ """Test case 1"""
+ self.start()
+ el = self.xpath('p[@align="left"]')
+ self.assertEqual("Left", el.text)
+ el = self.xpath('p[@align="right"]')
+ self.assertEqual("Right", el.text)
diff --git a/tests/appjs.py b/tests/appjs.py
new file mode 100644
index 00000000..02ac2193
--- /dev/null
+++ b/tests/appjs.py
@@ -0,0 +1,11 @@
+import unittest
+import base
+
+class Suite(base.Base):
+ def test_1(self):
+ """Test case 1"""
+ self.start()
+ el = self.xpath('button')
+ el.click()
+ alert = self.driver.switch_to.alert
+ self.assertEqual("3", alert.text)
diff --git a/tests/appjs.ur b/tests/appjs.ur
index 01e9f345..403b0b4e 100644
--- a/tests/appjs.ur
+++ b/tests/appjs.ur
@@ -1,5 +1,5 @@
fun id n = if n = 0 then 0 else 1 + id (n - 1)
fun main () : transaction page = return <xml><body>
- <button onclick={alert (show (id 3))}/>
+ <button onclick={fn _ => alert (show (id 3))}/>
</body></xml>
diff --git a/tests/ascdesc.py b/tests/ascdesc.py
new file mode 100644
index 00000000..6b514f4e
--- /dev/null
+++ b/tests/ascdesc.py
@@ -0,0 +1,11 @@
+import unittest
+import base
+
+class Suite(base.Base):
+ def test_1(self):
+ """Test case 1"""
+ self.start('Ascdesc/main')
+ el = self.xpath('p[1]')
+ self.assertEqual("1; 2; 3;", el.text)
+ el = self.xpath('p[2]')
+ self.assertEqual("3; 2; 1;", el.text)
diff --git a/tests/ascdesc.ur b/tests/ascdesc.ur
index 59dd0169..fadac27d 100644
--- a/tests/ascdesc.ur
+++ b/tests/ascdesc.ur
@@ -4,7 +4,15 @@ fun sortEm b =
queryX1 (SELECT * FROM t ORDER BY t.A {if b then sql_asc else sql_desc})
(fn r => <xml>{[r.A]}; </xml>)
-fun main () : transaction page = return <xml><body>
- <a link={sortEm True}>Ascending</a><br/>
- <a link={sortEm False}>Descending</a>
+task initialize = fn () =>
+ dml (INSERT INTO t (A) VALUES (1));
+ dml (INSERT INTO t (A) VALUES (2));
+ dml (INSERT INTO t (A) VALUES (3))
+
+fun main () : transaction page =
+ p1 <- sortEm True;
+ p2 <- sortEm False;
+ return <xml><body>
+ <p>{p1}</p>
+ <p>{p2}</p>
</body></xml>
diff --git a/tests/ascdesc.urp b/tests/ascdesc.urp
index 3e0b075d..a1c4124e 100644
--- a/tests/ascdesc.urp
+++ b/tests/ascdesc.urp
@@ -1,4 +1,3 @@
-database dbname=test
-sql ascdesc.sql
+database dbname=ascdesc
ascdesc \ No newline at end of file
diff --git a/tests/attrMangle.py b/tests/attrMangle.py
new file mode 100644
index 00000000..d3b24244
--- /dev/null
+++ b/tests/attrMangle.py
@@ -0,0 +1,11 @@
+import unittest
+import base
+
+class Suite(base.Base):
+ def test_1(self):
+ """Test case 1"""
+ self.start()
+ el = self.xpath('goofy[@name eq "beppo" and @data-role eq "excellence"]')
+ el.click()
+ alert = self.driver.switch_to.alert
+ self.assertEqual("You clicked it! That's some fancy shooting!", alert.text)
diff --git a/tests/attrs_escape.py b/tests/attrs_escape.py
new file mode 100644
index 00000000..fc9f91b5
--- /dev/null
+++ b/tests/attrs_escape.py
@@ -0,0 +1,10 @@
+import unittest
+import base
+
+class Suite(base.Base):
+ def test_1(self):
+ """Test case 1"""
+ self.start()
+ el = self.xpath('form/input')
+ val = el.get_attribute('value')
+ self.assertEqual("\"Well hey\"\nWow", val)
diff --git a/tests/attrs_escape.ur b/tests/attrs_escape.ur
index 12de101e..87d554fe 100644
--- a/tests/attrs_escape.ur
+++ b/tests/attrs_escape.ur
@@ -1,4 +1,6 @@
-val main = fn () => <html><body>
- <font face="\"Well hey\"
-Wow">Welcome</font>
-</body></html>
+fun main () : transaction page = return <xml><body>
+<form>
+ <submit value="\"Well hey\"
+Wow"/>
+</form>
+</body></xml>
diff --git a/tests/autocomp.py b/tests/autocomp.py
new file mode 100644
index 00000000..28c3b7d2
--- /dev/null
+++ b/tests/autocomp.py
@@ -0,0 +1,15 @@
+import unittest
+import base
+
+class Suite(base.Base):
+ def test_1(self):
+ """Test case 1"""
+ self.start()
+ txt = self.xpath('div')
+ self.assertEqual('/', txt.text)
+ inp = self.xpath('/input')
+ inp.send_keys('hello there')
+ self.assertEqual('hello there /', txt.text)
+ btn = self.xpath('button')
+ btn.click()
+ self.assertEqual("hello there / hello there", txt.text)
diff --git a/tests/autocomp.ur b/tests/autocomp.ur
index d4e6a287..753318f7 100644
--- a/tests/autocomp.ur
+++ b/tests/autocomp.ur
@@ -2,10 +2,10 @@ fun main () : transaction page =
a <- source "";
b <- source "";
return <xml><body>
- <form>
- <textbox{#A} source={a}/>
- <button onclick={x <- get a; set b x}/>
+ <ctextbox source={a}/>
+ <button onclick={fn _ => x <- get a; set b x}>click me</button>
+ <div>
<dyn signal={v <- signal a; return <xml>{[v]}</xml>}/>
/ <dyn signal={v <- signal b; return <xml>{[v]}</xml>}/>
- </form>
+ </div>
</body></xml>
diff --git a/tests/babySpawn.py b/tests/babySpawn.py
new file mode 100644
index 00000000..6693e969
--- /dev/null
+++ b/tests/babySpawn.py
@@ -0,0 +1,12 @@
+import unittest
+import base
+import time
+
+class Suite(base.Base):
+ def test_1(self):
+ """Test case 1"""
+ self.start()
+ btn = self.xpath('button')
+ btn.click()
+ alert = self.driver.switch_to.alert
+ self.assertEqual("Hi", alert.text)
diff --git a/tests/base.py b/tests/base.py
new file mode 100644
index 00000000..b9a026f2
--- /dev/null
+++ b/tests/base.py
@@ -0,0 +1,29 @@
+# use pip install selenium first
+# ensure you have both chome driver & chrome installed
+
+import unittest
+from selenium import webdriver
+from selenium.common.exceptions import NoSuchElementException
+
+class Base(unittest.TestCase):
+ """Include test cases on a given url"""
+
+ def start(self, path='main'):
+ self.driver.get('http://localhost:8080/' + path)
+ def xpath(self, path):
+ return self.driver.find_element_by_xpath('/html/body/'+path)
+ def body_text(self):
+ return self.driver.find_element_by_xpath('/html/body').text
+
+ def setUp(self):
+ """Start web driver"""
+ chrome_options = webdriver.ChromeOptions()
+ chrome_options.add_argument('--no-sandbox')
+ chrome_options.add_argument('--headless')
+ chrome_options.add_argument('--disable-gpu')
+ self.driver = webdriver.Chrome(options=chrome_options)
+ self.driver.implicitly_wait(10)
+
+ def tearDown(self):
+ """Stop web driver"""
+ self.driver.quit()
diff --git a/tests/bindpat.py b/tests/bindpat.py
new file mode 100644
index 00000000..6c33f52f
--- /dev/null
+++ b/tests/bindpat.py
@@ -0,0 +1,9 @@
+import unittest
+import base
+
+class Suite(base.Base):
+ def test_1(self):
+ """Test case 1"""
+ self.driver.get('http://localhost:8080/main')
+ el = self.driver.find_element_by_xpath('/html/body')
+ self.assertEqual("1, 2, hi, 2.34, 8, 9", el.text)
diff --git a/tests/bindpat.ur b/tests/bindpat.ur
index bca4bd41..8fd6eb39 100644
--- a/tests/bindpat.ur
+++ b/tests/bindpat.ur
@@ -1,6 +1,9 @@
fun main () : transaction page =
(a, b) <- return (1, 2);
{C = c, ...} <- return {C = "hi", D = False};
- d <- return 2.34;
- {1 = e, 2 = f} <- return (8, 9);
+ let
+ val d = 2.34
+ val {1 = e, 2 = f} = (8, 9)
+ in
return <xml>{[a]}, {[b]}, {[c]}, {[d]}, {[e]}, {[f]}</xml>
+ end \ No newline at end of file
diff --git a/tests/driver.sh b/tests/driver.sh
new file mode 100755
index 00000000..cc62644b
--- /dev/null
+++ b/tests/driver.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+
+if [[ $# -eq 0 ]] ; then
+ echo 'Supply at least one argument'
+ exit 1
+fi
+
+TESTDB=/tmp/$1.db
+TESTSQL=/tmp/$1.sql
+TESTPID=/tmp/$1.pid
+TESTSRV=./$1.exe
+
+rm -f $TESTDB $TESTSQL $TESTPID $TESTSRV
+../bin/urweb -debug -boot -noEmacs -dbms sqlite -db $TESTDB -sql $TESTSQL "$1" || exit 1
+
+if [ -e $TESTSQL ]
+then
+ sqlite3 $TESTDB < $TESTSQL
+fi
+
+$TESTSRV -q -a 127.0.0.1 &
+echo $! >> $TESTPID
+sleep 1
+python -m unittest $1.py
+kill `cat $TESTPID`
diff --git a/tests/entities.py b/tests/entities.py
new file mode 100644
index 00000000..d9087cbf
--- /dev/null
+++ b/tests/entities.py
@@ -0,0 +1,14 @@
+import unittest
+import base
+
+class Suite(base.Base):
+ def test_1(self):
+ """Test case 1"""
+ self.start()
+ p = self.xpath('p[1]')
+ self.assertEqual('Hello world! & so on, © me today (8 €)', p.text)
+ p = self.xpath('p[2]')
+ self.assertEqual('♠ ♣ ♥ ♦', p.text)
+ p = self.xpath('p[3]')
+ self.assertEqual('† DANGER †', p.text)
+
diff --git a/tests/entities.ur b/tests/entities.ur
index 8b78edbc..1f45520d 100644
--- a/tests/entities.ur
+++ b/tests/entities.ur
@@ -1,5 +1,5 @@
fun main () : transaction page = return <xml><body>
- Hello world! &amp; so on, &copy; me today (8 &euro;)<br/>
- &spades; &clubs; &hearts; &diams;<br/>
- &dagger; DANGER &dagger;
+ <p>Hello world! &amp; so on, &copy; me today (8 &euro;)</p>
+ <p>&spades; &clubs; &hearts; &diams;</p>
+ <p>&dagger; DANGER &dagger;</p>
</body></xml>
diff --git a/tests/fact.py b/tests/fact.py
new file mode 100644
index 00000000..3dcd6f71
--- /dev/null
+++ b/tests/fact.py
@@ -0,0 +1,10 @@
+import unittest
+import base
+
+class Suite(base.Base):
+ def test_1(self):
+ """Test case 1"""
+ self.start()
+ b = self.driver.find_element_by_xpath('/html/body')
+ self.assertEqual('3628800, 3628800', b.text)
+
diff --git a/tests/filter.py b/tests/filter.py
new file mode 100644
index 00000000..f68f8f88
--- /dev/null
+++ b/tests/filter.py
@@ -0,0 +1,9 @@
+import unittest
+import base
+
+class Suite(base.Base):
+ def test_1(self):
+ """Test case 1"""
+ self.start('Filter/main')
+ tx = self.body_text()
+ self.assertEqual("4, 4; 44, 4.4;", tx)
diff --git a/tests/filter.ur b/tests/filter.ur
index efd326c3..2691a939 100644
--- a/tests/filter.ur
+++ b/tests/filter.ur
@@ -1,9 +1,16 @@
-fun filter [fs ::: {Type}] [ks] (t : sql_table fs ks) (p : sql_exp [T = fs] [] [] bool)
- : sql_query [T = fs] [] =
+fun filter [fs ::: {Type}] [ks] (t : sql_table fs ks) (p : sql_exp [T = fs] [] [] bool) =
(SELECT * FROM t WHERE {p})
table t : { A : int, B : float }
-fun main () =
- queryX (filter t (WHERE t.A > 3))
- (fn r => <xml>{[r.T.A]}, {[r.T.B]}</xml>)
+task initialize = fn () =>
+ dml (INSERT INTO t (A, B) VALUES (1, 2.0));
+ dml (INSERT INTO t (A, B) VALUES (2, 1.0));
+ dml (INSERT INTO t (A, B) VALUES (3, 3.0));
+ dml (INSERT INTO t (A, B) VALUES (4, 4.0));
+ dml (INSERT INTO t (A, B) VALUES (44, 4.4))
+
+fun main () : transaction page =
+ r <- queryX (filter t (WHERE t.A > 3))
+ (fn r => <xml>{[r.T.A]}, {[r.T.B]}; </xml>);
+ return <xml><body>{r}</body></xml>
diff --git a/tests/jsbspace.py b/tests/jsbspace.py
new file mode 100644
index 00000000..b29d44b9
--- /dev/null
+++ b/tests/jsbspace.py
@@ -0,0 +1,11 @@
+import unittest
+import base
+
+class Suite(base.Base):
+ def test_1(self):
+ """Test case 1"""
+ self.start()
+ el = self.xpath('button')
+ el.click()
+ alert = self.driver.switch_to.alert
+ self.assertEqual('Some \btext', alert.text)
diff --git a/tests/jsbspace.ur b/tests/jsbspace.ur
new file mode 100644
index 00000000..bf4b824f
--- /dev/null
+++ b/tests/jsbspace.ur
@@ -0,0 +1,12 @@
+fun main () : transaction page =
+let
+ fun onclick (): transaction unit =
+ (* this function runs on the client *)
+ alert "Some \btext"
+in
+return <xml>
+ <body>
+ <button onclick={fn _ => onclick()}>Click me!</button>
+ </body>
+</xml>
+end \ No newline at end of file
diff --git a/tests/jsonTest.py b/tests/jsonTest.py
new file mode 100644
index 00000000..d9147511
--- /dev/null
+++ b/tests/jsonTest.py
@@ -0,0 +1,16 @@
+import unittest
+import base
+
+class Suite(base.Base):
+ def test_1(self):
+ """Test case 1"""
+ self.start()
+
+ pre = self.xpath('pre[1]')
+ self.assertEqual('line 1\nline 2', pre.text)
+
+ pre = self.xpath('pre[2]')
+ self.assertEqual('1 :: 2 :: 3 :: []', pre.text)
+
+ pre = self.xpath('pre[3]')
+ self.assertEqual('["hi","bye\\"","hehe"]', pre.text)
diff --git a/tests/listGroupBy.ur b/tests/listGroupBy.ur
new file mode 100644
index 00000000..c2419ce1
--- /dev/null
+++ b/tests/listGroupBy.ur
@@ -0,0 +1,13 @@
+fun lister () = List.tabulateM (fn _ => n <- rand; return (n % 100)) 8
+
+fun main () : transaction page =
+ inp <- source [];
+ return <xml><body>
+ <button value="Compute" onclick={fn _ =>
+ ls <- rpc (lister ());
+ set inp ls}/>
+
+ <dyn signal={inp <- signal inp; return (txt inp)}/>
+ -&gt;
+ <dyn signal={inp <- signal inp; return (txt (List.groupBy (fn n m => n % 2 = m % 2) inp))}/>
+ </body></xml>
diff --git a/tests/listGroupBy.urp b/tests/listGroupBy.urp
new file mode 100644
index 00000000..1a63a89d
--- /dev/null
+++ b/tests/listGroupBy.urp
@@ -0,0 +1,4 @@
+rewrite all ListGroupBy/*
+
+$/list
+listGroupBy \ No newline at end of file
diff --git a/tests/pairUnify.ur b/tests/pairUnify.ur
new file mode 100644
index 00000000..1c9f9759
--- /dev/null
+++ b/tests/pairUnify.ur
@@ -0,0 +1,6 @@
+datatype a = A
+datatype b = B
+
+val x : a * b = (A, B)
+
+val y : b = x
diff --git a/tests/unurlify2.ur b/tests/unurlify2.ur
new file mode 100644
index 00000000..2e82928d
--- /dev/null
+++ b/tests/unurlify2.ur
@@ -0,0 +1,16 @@
+datatype bugged = Nothing | Something of int
+datatype myDt = One | Two
+type myRecord = {Bugged: bugged
+ , MyDt : myDt}
+
+fun rpcTarget (t: myRecord) = return ()
+
+val good = {Bugged = Something 4, MyDt = One}
+val bad = {Bugged = Nothing, MyDt = One}
+
+fun main () : transaction page = return <xml>
+ <body>
+ <button onclick={fn _ => rpc (rpcTarget good)}>rpc with good</button>
+ <button onclick={fn _ => rpc (rpcTarget bad)}>rpc with bad</button>
+ </body>
+</xml>