From 34196bdad72334f9d8f809d6c9f564667f6011d4 Mon Sep 17 00:00:00 2001 From: Adam Chlipala Date: Thu, 27 Jul 2017 15:30:58 -0400 Subject: Add test cases for last commit --- tests/slashform.ur | 8 ++++++++ tests/slashform.urs | 1 + 2 files changed, 9 insertions(+) create mode 100644 tests/slashform.ur create mode 100644 tests/slashform.urs (limited to 'tests') diff --git a/tests/slashform.ur b/tests/slashform.ur new file mode 100644 index 00000000..d5993a36 --- /dev/null +++ b/tests/slashform.ur @@ -0,0 +1,8 @@ +fun handler f = return {[f.F1]} {[f.F2]} + +val main = return
+ + + + +
diff --git a/tests/slashform.urs b/tests/slashform.urs new file mode 100644 index 00000000..61778b87 --- /dev/null +++ b/tests/slashform.urs @@ -0,0 +1 @@ +val main : transaction page -- cgit v1.2.3 From 53dbce6998e78ddcb05693c7efdca101075941b0 Mon Sep 17 00:00:00 2001 From: Adam Chlipala Date: Thu, 27 Jul 2017 20:08:01 -0400 Subject: Fix last fix, to handle checkboxes properly --- src/cjr_print.sml | 7 ++++++- tests/slashform.ur | 3 ++- 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/src/cjr_print.sml b/src/cjr_print.sml index 8fafc59f..1fdb45d9 100644 --- a/src/cjr_print.sml +++ b/src/cjr_print.sml @@ -482,6 +482,11 @@ fun isFile (t : typ) = TFfi ("Basis", "file") => true | _ => false +fun isString (t : typ) = + case #1 t of + TFfi ("Basis", "string") => true + | _ => false + fun p_sql_type t = string (Settings.p_sql_ctype t) fun getPargs (e, _) = @@ -2955,7 +2960,7 @@ fun p_file env (ds, ps) = space, string "=", space, - if includesFile then + if includesFile andalso isString t then string "request" else unurlify true env t, diff --git a/tests/slashform.ur b/tests/slashform.ur index d5993a36..63591886 100644 --- a/tests/slashform.ur +++ b/tests/slashform.ur @@ -1,8 +1,9 @@ -fun handler f = return {[f.F1]} {[f.F2]} +fun handler f = return {[f.F1]} {[f.F2]} {[f.F3]} val main = return
+
-- cgit v1.2.3 From b1a6440a3fb285cdfd5301510b96b1ef3b96c050 Mon Sep 17 00:00:00 2001 From: Adam Chlipala Date: Sun, 1 Oct 2017 17:13:17 -0400 Subject: New .urp directives: mimeTypes and long form of file --- doc/manual.tex | 2 ++ src/compiler.sig | 3 ++- src/compiler.sml | 31 ++++++++++++++++++++++--------- src/demo.sml | 3 ++- src/settings.sig | 5 ++++- src/settings.sml | 13 +++++++++---- tests/fake_types | 2 ++ tests/mimeTypesDirective.ur | 0 tests/mimeTypesDirective.urp | 6 ++++++ 9 files changed, 49 insertions(+), 16 deletions(-) create mode 100644 tests/fake_types create mode 100644 tests/mimeTypesDirective.ur create mode 100644 tests/mimeTypesDirective.urp (limited to 'tests') diff --git a/doc/manual.tex b/doc/manual.tex index eaf7aab5..1b476499 100644 --- a/doc/manual.tex +++ b/doc/manual.tex @@ -150,6 +150,7 @@ Here is the complete list of directive forms. ``FFI'' stands for ``foreign func \item \texttt{effectful Module.ident} registers an FFI function or transaction as having side effects. The optimizer avoids removing, moving, or duplicating calls to such functions. This is the default behavior for \texttt{transaction}-based types. \item \texttt{exe FILENAME} sets the filename to which to write the output executable. The default for file \texttt{P.urp} is \texttt{P.exe}. \item \texttt{file URI FILENAME} asks for the application executable to respond to requests for \texttt{URI} by serving a snapshot of the contents of \texttt{FILENAME} as of compile time. That is, the file contents are baked into the executable. System file \texttt{/etc/mime.types} is consulted (again, at compile time) to figure out the right MIME type to suggest in the HTTP response. +\item \texttt{file URI FILENAME MIME-TYPE} works like the simpler form of \texttt{file}, but the proper MIME type for the file is given directly. \item \texttt{ffi FILENAME} reads the file \texttt{FILENAME.urs} to determine the interface to a new FFI module. The name of the module is calculated from \texttt{FILENAME} in the same way as for normal source files. See the files \texttt{include/urweb/urweb\_cpp.h} and \texttt{src/c/urweb.c} for examples of C headers and implementations for FFI modules. In general, every type or value \texttt{Module.ident} becomes \texttt{uw\_Module\_ident} in C. \item \texttt{html5} asks to generate HTML5 code, which primarily affects the first few lines of the output documents, like the \texttt{DOCTYPE}. This option is on by default. \item \texttt{include FILENAME} adds \texttt{FILENAME} to the list of files to be \texttt{\#include}d in C sources. This is most useful for interfacing with new FFI modules. @@ -176,6 +177,7 @@ Here is the complete list of directive forms. ``FFI'' stands for ``foreign func \end{itemize} \item \texttt{link FILENAME} adds \texttt{FILENAME} to the list of files to be passed to the linker at the end of compilation. This is most useful for importing extra libraries needed by new FFI modules. \item \texttt{linker CMD} sets \texttt{CMD} as the command line prefix to use for linking C object files. The command line will be completed with a space-separated list of \texttt{.o} and \texttt{.a} files, \texttt{-L} and \texttt{-l} flags, and finally with a \texttt{-o} flag to set the location where the executable should be written. +\item \texttt{mimeTypes PATH} sets the name of the file from which the MIME-type database is read, as a substitute for the usual \texttt{/etc/mime.types} on UNIX systems. \item \texttt{minHeap NUMBYTES} sets the initial size for thread-local heaps used in handling requests. These heaps grow automatically as needed (up to any maximum set with \texttt{limit}), but each regrow requires restarting the request handling process. \item \texttt{monoInline TREESIZE} sets how many nodes the AST of a function definition may have before the optimizer stops trying hard to inline calls to that function. (This is one of two options for one of two intermediate languages within the compiler.) \item \texttt{neverInline PATH} requests that no call to the referenced function be inlined. Section \ref{structure} explains how functions are assigned path strings. diff --git a/src/compiler.sig b/src/compiler.sig index 952c7070..0ff84f1c 100644 --- a/src/compiler.sig +++ b/src/compiler.sig @@ -62,7 +62,8 @@ signature COMPILER = sig sigFile : string option, safeGets : string list, onError : (string * string list * string) option, - minHeap : int + minHeap : int, + mimeTypes : string option } val compile : string -> bool val compiler : string -> unit diff --git a/src/compiler.sml b/src/compiler.sml index c13de304..3fb0b767 100644 --- a/src/compiler.sml +++ b/src/compiler.sml @@ -66,7 +66,8 @@ type job = { sigFile : string option, safeGets : string list, onError : (string * string list * string) option, - minHeap : int + minHeap : int, + mimeTypes : string option } type ('src, 'dst) phase = { @@ -386,7 +387,8 @@ fun institutionalizeJob (job : job) = Settings.setSafeGets (#safeGets job); Settings.setOnError (#onError job); Settings.setMinHeap (#minHeap job); - Settings.setSigFile (#sigFile job)) + Settings.setSigFile (#sigFile job); + Settings.setMimeFilePath (Option.getOpt (#mimeTypes job, "/etc/mime.types"))) datatype commentableLine = EndOfFile @@ -467,7 +469,8 @@ fun parseUrp' accLibs fname = sigFile = NONE, safeGets = [], onError = NONE, - minHeap = 0} + minHeap = 0, + mimeTypes = NONE} in institutionalizeJob job; {Job = job, Libs = []} @@ -601,6 +604,7 @@ fun parseUrp' accLibs fname = val safeGets = ref [] val onError = ref NONE val minHeap = ref 0 + val mimeTypes = ref NONE fun finish sources = let @@ -638,7 +642,8 @@ fun parseUrp' accLibs fname = sigFile = !sigFile, safeGets = rev (!safeGets), onError = !onError, - minHeap = !minHeap + minHeap = !minHeap, + mimeTypes = !mimeTypes } fun mergeO f (old, new) = @@ -699,7 +704,8 @@ fun parseUrp' accLibs fname = sigFile = mergeO #2 (#sigFile old, #sigFile new), safeGets = #safeGets old @ #safeGets new, onError = mergeO #2 (#onError old, #onError new), - minHeap = Int.max (#minHeap old, #minHeap new) + minHeap = Int.max (#minHeap old, #minHeap new), + mimeTypes = mergeO #2 (#mimeTypes old, #mimeTypes new) } in if accLibs then @@ -914,13 +920,20 @@ fun parseUrp' accLibs fname = | "html5" => Settings.setIsHtml5 true | "xhtml" => Settings.setIsHtml5 false | "lessSafeFfi" => Settings.setLessSafeFfi true + | "mimeTypes" => Settings.setMimeFilePath (relify arg) | "file" => (case String.fields Char.isSpace arg of - [uri, fname] => (Settings.setFilePath thisPath; - Settings.addFile {Uri = uri, - LoadFromFilename = fname}; - url := {action = Settings.Allow, kind = Settings.Exact, pattern = uri} :: !url) + uri :: fname :: rest => + (Settings.setFilePath thisPath; + Settings.addFile {Uri = uri, + LoadFromFilename = fname, + MimeType = case rest of + [] => NONE + | [ty] => SOME ty + | _ => (ErrorMsg.error "Bad 'file' arguments"; + NONE)}; + url := {action = Settings.Allow, kind = Settings.Exact, pattern = uri} :: !url) | _ => ErrorMsg.error "Bad 'file' arguments") | "jsFile" => diff --git a/src/demo.sml b/src/demo.sml index 62b9037a..a682d28d 100644 --- a/src/demo.sml +++ b/src/demo.sml @@ -125,7 +125,8 @@ fun make' {prefix, dirname, guided} = sigFile = mergeWith #2 (#sigFile combined, #sigFile urp), safeGets = #safeGets combined @ #safeGets urp, onError = NONE, - minHeap = 0 + minHeap = 0, + mimeTypes = mergeWith #2 (#mimeTypes combined, #mimeTypes urp) } val parse = Compiler.run (Compiler.transform Compiler.parseUrp "Demo parseUrp") diff --git a/src/settings.sig b/src/settings.sig index 256a12b5..729218ac 100644 --- a/src/settings.sig +++ b/src/settings.sig @@ -298,7 +298,7 @@ signature SETTINGS = sig val setFilePath : string -> unit (* Sets the directory where we look for files being added below. *) - val addFile : {Uri : string, LoadFromFilename : string} -> unit + val addFile : {Uri : string, LoadFromFilename : string, MimeType : string option} -> unit val listFiles : unit -> {Uri : string, ContentType : string option, LastModified : Time.time, Bytes : Word8Vector.vector} list val addJsFile : string (* filename *) -> unit @@ -306,4 +306,7 @@ signature SETTINGS = sig val setOutputJsFile : string option (* filename *) -> unit val getOutputJsFile : unit -> string option + + val setMimeFilePath : string -> unit + (* Set unusual location for /etc/mime.types. *) end diff --git a/src/settings.sml b/src/settings.sml index a3263c06..d3ac99d4 100644 --- a/src/settings.sml +++ b/src/settings.sml @@ -843,14 +843,17 @@ structure SM = BinaryMapFn(struct val noMimeFile = ref false +val mimeFilePath = ref "/etc/mime.types" +fun setMimeFilePath file = mimeFilePath := file + fun noMime () = - (TextIO.output (TextIO.stdErr, "WARNING: Error opening /etc/mime.types. Static files will be served with no suggested MIME types.\n"); + (TextIO.output (TextIO.stdErr, "WARNING: Error opening " ^ !mimeFilePath ^ ". Static files will be served with no suggested MIME types.\n"); noMimeFile := true; SM.empty) fun readMimeTypes () = let - val inf = FileIO.txtOpenIn "/etc/mime.types" + val inf = FileIO.txtOpenIn (!mimeFilePath) fun loop m = case TextIO.inputLine inf of @@ -908,7 +911,7 @@ val filePath = ref "." fun setFilePath path = filePath := path -fun addFile {Uri, LoadFromFilename} = +fun addFile {Uri, LoadFromFilename, MimeType} = let val path = OS.Path.concat (!filePath, LoadFromFilename) in @@ -926,7 +929,9 @@ fun addFile {Uri, LoadFromFilename} = Uri, (path, {Uri = Uri, - ContentType = mimeTypeOf path, + ContentType = case MimeType of + NONE => mimeTypeOf path + | _ => MimeType, LastModified = OS.FileSys.modTime path, Bytes = BinIO.inputAll inf})); BinIO.closeIn inf diff --git a/tests/fake_types b/tests/fake_types new file mode 100644 index 00000000..405e9d1d --- /dev/null +++ b/tests/fake_types @@ -0,0 +1,2 @@ +horrible_idea/blorpapalooza txt +whoa/yowza html diff --git a/tests/mimeTypesDirective.ur b/tests/mimeTypesDirective.ur new file mode 100644 index 00000000..e69de29b diff --git a/tests/mimeTypesDirective.urp b/tests/mimeTypesDirective.urp new file mode 100644 index 00000000..43f06a00 --- /dev/null +++ b/tests/mimeTypesDirective.urp @@ -0,0 +1,6 @@ +mimeTypes fake_types +file /hello.txt hello.txt +file /hello.html hello.html +file /hello2.txt hello.txt gadzooks/yippie + +mimeTypesDirective -- cgit v1.2.3 From 2bc51bd866b52bc738f259ffe6e9fb8f6068a6b6 Mon Sep 17 00:00:00 2001 From: "majorseitan@blockfreie.org" Date: Sat, 14 Apr 2018 21:56:09 -0400 Subject: Handling of JSON escape characters 1. Handle escape sequence chars \t \n \r 2. Fail on unsupported escape characters. Instead of skipping \ on unsupported sequences it now fails. --- lib/ur/json.ur | 22 +++++++++++++++++----- tests/jsonTest.ur | 1 + 2 files changed, 18 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/lib/ur/json.ur b/lib/ur/json.ur index 9288a6dd..1e3e3f39 100644 --- a/lib/ur/json.ur +++ b/lib/ur/json.ur @@ -46,10 +46,14 @@ fun escape s = let val ch = String.sub s 0 in - (if ch = #"\"" || ch = #"\\" then - "\\" ^ String.str ch - else - String.str ch) ^ esc (String.suffix s 1) + (case ch of + #"\n" => "\\n" + | #"\r" => "\\r" + | #"\t" => "\\t" + | #"\"" => "\\\"" + | #"\'" => "\\\'" + | x => String.str ch + ) ^ esc (String.suffix s 1) end in "\"" ^ esc s @@ -90,7 +94,15 @@ fun unescape s = if i+1 >= len then error JSON unescape: Bad escape sequence: {[s]} else - String.str (String.sub s (i+1)) ^ unesc (i+2) + (case String.sub s (i+1) of + #"n" => "\n" + | #"r" => "\r" + | #"t" => "\t" + | #"\"" => "\"" + | #"\'" => "\'" + | x => error JSON unescape: Bad escape char: {[x]}) + ^ + unesc (i+2) | _ => String.str ch ^ unesc (i+1) end in diff --git a/tests/jsonTest.ur b/tests/jsonTest.ur index 97898de8..1be6e7b5 100644 --- a/tests/jsonTest.ur +++ b/tests/jsonTest.ur @@ -1,6 +1,7 @@ open Json fun main () : transaction page = return +
{[ fromJson "\"line 1\\nline 2\"" : string ]}

{[fromJson "[1, 2, 3]" : list int]}
{[toJson ("hi" :: "bye\"" :: "hehe" :: [])]}
-- cgit v1.2.3 From e2552a79ed87721a81c246b9cfd053701d665f25 Mon Sep 17 00:00:00 2001 From: "majorseitan@blockfreie.org" Date: Sun, 15 Apr 2018 16:20:31 -0400 Subject: Handling of JSON escape characters 1. Handle the escape character \\ --- lib/ur/json.ur | 2 ++ tests/jsonTest.ur | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/lib/ur/json.ur b/lib/ur/json.ur index 1e3e3f39..7ebb010f 100644 --- a/lib/ur/json.ur +++ b/lib/ur/json.ur @@ -52,6 +52,7 @@ fun escape s = | #"\t" => "\\t" | #"\"" => "\\\"" | #"\'" => "\\\'" + | #"\\" => "\\\\" | x => String.str ch ) ^ esc (String.suffix s 1) end @@ -100,6 +101,7 @@ fun unescape s = | #"t" => "\t" | #"\"" => "\"" | #"\'" => "\'" + | #"\\" => "\\" | x => error JSON unescape: Bad escape char: {[x]}) ^ unesc (i+2) diff --git a/tests/jsonTest.ur b/tests/jsonTest.ur index 1be6e7b5..071cf34b 100644 --- a/tests/jsonTest.ur +++ b/tests/jsonTest.ur @@ -1,7 +1,7 @@ open Json fun main () : transaction page = return -
{[ fromJson "\"line 1\\nline 2\"" : string ]}

+
{[ fromJson "\"\\\\line 1\\nline 2\"" : string ]}

{[fromJson "[1, 2, 3]" : list int]}
{[toJson ("hi" :: "bye\"" :: "hehe" :: [])]}
-- cgit v1.2.3 From c293746d4c34ccb7abb8af41f7d05940aa7e4076 Mon Sep 17 00:00:00 2001 From: Artyom Shalkhakov Date: Tue, 8 May 2018 16:03:24 +0600 Subject: Adding Selenium-based checking to tests. --- .gitignore | 5 +++++ tests/DynChannel.py | 20 ++++++++++++++++++++ tests/Makefile | 17 +++++++++++++++++ tests/alert.py | 11 +++++++++++ tests/alert.ur | 2 +- tests/alert.urp | 3 --- tests/align.py | 11 +++++++++++ tests/appjs.py | 11 +++++++++++ tests/appjs.ur | 2 +- tests/ascdesc.py | 11 +++++++++++ tests/ascdesc.ur | 14 +++++++++++--- tests/ascdesc.urp | 3 +-- tests/attrMangle.py | 11 +++++++++++ tests/attrs_escape.py | 10 ++++++++++ tests/attrs_escape.ur | 10 ++++++---- tests/autocomp.py | 15 +++++++++++++++ tests/autocomp.ur | 8 ++++---- tests/base.py | 29 +++++++++++++++++++++++++++++ tests/bindpat.py | 9 +++++++++ tests/bindpat.ur | 7 +++++-- tests/driver.sh | 25 +++++++++++++++++++++++++ tests/entities.py | 14 ++++++++++++++ tests/entities.ur | 6 +++--- tests/fact.py | 10 ++++++++++ tests/filter.py | 9 +++++++++ tests/filter.ur | 17 ++++++++++++----- tests/jsonTest.py | 16 ++++++++++++++++ tests/jsonTest.ur | 4 ++-- 28 files changed, 280 insertions(+), 30 deletions(-) create mode 100644 tests/DynChannel.py create mode 100644 tests/alert.py delete mode 100644 tests/alert.urp create mode 100644 tests/align.py create mode 100644 tests/appjs.py create mode 100644 tests/ascdesc.py create mode 100644 tests/attrMangle.py create mode 100644 tests/attrs_escape.py create mode 100644 tests/autocomp.py create mode 100644 tests/base.py create mode 100644 tests/bindpat.py create mode 100755 tests/driver.sh create mode 100644 tests/entities.py create mode 100644 tests/fact.py create mode 100644 tests/filter.py create mode 100644 tests/jsonTest.py (limited to 'tests') 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/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..63ae555e 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -2,3 +2,20 @@ all: test.o test.o: test.c gcc -c test.c -o test.o +### + +simple:: + ./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 bindpat + ./driver.sh DynChannel + ./driver.sh jsonTest + ./driver.sh entities + ./driver.sh fact + ./driver.sh filter 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 - Click Me! + alert "You clicked it! That's some fancy shooting!"}>Click Me! 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 - +
{[v]}}/> / {[v]}}/> - +
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 {[a]}, {[b]}, {[c]}, {[d]}, {[e]}, {[f]} + 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 - Hello world! & so on, © me today (8 €)
- ♠ ♣ ♥ ♦
- † DANGER † +

Hello world! & so on, © me today (8 €)

+

♠ ♣ ♥ ♦

+

† DANGER †

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 => {[r.T.A]}, {[r.T.B]}) +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 => {[r.T.A]}, {[r.T.B]}; ); + return {r} 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/jsonTest.ur b/tests/jsonTest.ur index 1be6e7b5..38d0d201 100644 --- a/tests/jsonTest.ur +++ b/tests/jsonTest.ur @@ -2,6 +2,6 @@ open Json fun main () : transaction page = return
{[ fromJson "\"line 1\\nline 2\"" : string ]}

- {[fromJson "[1, 2, 3]" : list int]}
- {[toJson ("hi" :: "bye\"" :: "hehe" :: [])]} +
{[fromJson "[1, 2, 3]" : list int]}

+
{[toJson ("hi" :: "bye\"" :: "hehe" :: [])]}
-- cgit v1.2.3 From 1078553f5a8de2a5e85dbd49058370afeefa68c7 Mon Sep 17 00:00:00 2001 From: Artyom Shalkhakov Date: Tue, 8 May 2018 17:25:29 +0600 Subject: Adding jsbspace for #121. --- tests/Makefile | 1 + tests/jsbspace.py | 11 +++++++++++ tests/jsbspace.ur | 12 ++++++++++++ 3 files changed, 24 insertions(+) create mode 100644 tests/jsbspace.py create mode 100644 tests/jsbspace.ur (limited to 'tests') diff --git a/tests/Makefile b/tests/Makefile index 63ae555e..250a2ece 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -19,3 +19,4 @@ simple:: ./driver.sh entities ./driver.sh fact ./driver.sh filter + ./driver.sh jsbspace 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 + + + + +end \ No newline at end of file -- cgit v1.2.3 From 4c01511f5bf2229da7b146943444278d714ed7d6 Mon Sep 17 00:00:00 2001 From: Artyom Shalkhakov Date: Tue, 15 May 2018 21:48:15 +0600 Subject: More tests. --- tests/Makefile | 8 ++++++++ tests/aborter.py | 11 +++++++++++ tests/aborter.urp | 1 + tests/aborter2.py | 11 +++++++++++ tests/active.py | 14 ++++++++++++++ tests/activeBlock.py | 20 ++++++++++++++++++++ tests/activeBlock.ur | 2 +- tests/activeEmpty.py | 12 ++++++++++++ tests/activeFocus.py | 18 ++++++++++++++++++ tests/activeFocus.ur | 2 +- tests/agg.py | 8 ++++++++ tests/agg.ur | 20 +++++++++++++++----- tests/ahead.py | 15 +++++++++++++++ tests/babySpawn.py | 12 ++++++++++++ 14 files changed, 147 insertions(+), 7 deletions(-) create mode 100644 tests/aborter.py create mode 100644 tests/aborter2.py create mode 100644 tests/active.py create mode 100644 tests/activeBlock.py create mode 100644 tests/activeEmpty.py create mode 100644 tests/activeFocus.py create mode 100644 tests/agg.py create mode 100644 tests/ahead.py create mode 100644 tests/babySpawn.py (limited to 'tests') diff --git a/tests/Makefile b/tests/Makefile index 250a2ece..ecf5557b 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -5,6 +5,13 @@ test.o: test.c ### 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 @@ -13,6 +20,7 @@ simple:: ./driver.sh attrs_escape echo ./driver.sh attrs ./driver.sh autocomp + ./driver.sh babySpawn ./driver.sh bindpat ./driver.sh DynChannel ./driver.sh jsonTest 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 ", 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 - }/> Hi!}/> 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 = Done}/> - }/> + }>Click 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 => {[r.1]};); xml2 <- queryX q4 (fn r => {[r.1]}, {[r.2]};); 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/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) -- cgit v1.2.3 From 30edae2956d346e7df7ca27fcc77432e45cea99e Mon Sep 17 00:00:00 2001 From: Adam Chlipala Date: Sat, 19 May 2018 16:06:11 -0400 Subject: More defensive unurlification of enumerations (closes #117) --- src/cjr_print.sml | 5 ++++- tests/unurlify2.ur | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 tests/unurlify2.ur (limited to 'tests') 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/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 + + + + + -- cgit v1.2.3 From 3e8fc5122fed2baee3a4d27d51575f6dd5174ea8 Mon Sep 17 00:00:00 2001 From: Adam Chlipala Date: Sun, 20 May 2018 19:28:17 -0400 Subject: Proper error message when the body of a 'val' declaration fails to check against the type annotation --- src/elaborate.sml | 3 ++- tests/pairUnify.ur | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 tests/pairUnify.ur (limited to 'tests') 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/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 -- cgit v1.2.3 From 373cb403871c0c77f26cb76213adde3aeb278240 Mon Sep 17 00:00:00 2001 From: Adam Chlipala Date: Mon, 21 May 2018 18:53:38 -0400 Subject: A test for List.groupBy --- tests/listGroupBy.ur | 13 +++++++++++++ tests/listGroupBy.urp | 4 ++++ 2 files changed, 17 insertions(+) create mode 100644 tests/listGroupBy.ur create mode 100644 tests/listGroupBy.urp (limited to 'tests') 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 +