summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--.github/workflows/tests.yml13
-rw-r--r--Cargo.lock295
-rw-r--r--README.md4
-rw-r--r--dhall/Cargo.toml12
-rw-r--r--dhall/build.rs344
-rw-r--r--dhall/src/error/mod.rs11
-rw-r--r--dhall/src/lib.rs2
-rw-r--r--dhall/src/tests.rs360
-rw-r--r--dhall/tests/spec.rs713
9 files changed, 1014 insertions, 740 deletions
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 5d8236c..e5b800e 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -79,11 +79,8 @@ jobs:
- name: Run tests
run: wasm-pack test serde_dhall --node
- # Tests don't work because of path escaping shenanigans. I have a branch that
- # will fix that but depends on https://github.com/LukasKalbertodt/libtest-mimic/pull/4
test_windows:
- name: Build on windows
- # name: Run tests on windows
+ name: Run tests on windows
runs-on: windows-latest
strategy:
matrix:
@@ -100,11 +97,7 @@ jobs:
profile: minimal
toolchain: ${{ matrix.rust }}
override: true
- - name: Build
+ - name: Run tests
uses: actions-rs/cargo@v1
with:
- command: build
- # - name: Run tests
- # uses: actions-rs/cargo@v1
- # with:
- # command: test
+ command: test
diff --git a/Cargo.lock b/Cargo.lock
index ff10b49..ec75c09 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -40,11 +40,26 @@ dependencies = [
]
[[package]]
+name = "anyhow"
+version = "1.0.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "arrayvec"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "atty"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "hermit-abi 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "autocfg"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -123,6 +138,30 @@ version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "clap"
+version = "2.33.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "vec_map 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "colored-diff"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "itertools 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "console_error_panic_hook"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -146,12 +185,56 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
-name = "ctor"
-version = "0.1.15"
+name = "crossbeam-channel"
+version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
- "syn 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "crossbeam-epoch 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "memoffset 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "crossbeam-queue"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -160,15 +243,17 @@ version = "0.5.3"
dependencies = [
"abnf_to_pest 0.5.0",
"annotate-snippets 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "anyhow 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)",
+ "colored-diff 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"hex 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libtest-mimic 0.2.0 (git+https://github.com/LukasKalbertodt/libtest-mimic)",
"once_cell 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pest 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"pest_consume 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
"pest_generator 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
"reqwest 0.10.6 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -379,6 +464,14 @@ version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "heck"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "hermit-abi"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -478,6 +571,14 @@ dependencies = [
[[package]]
name = "itertools"
+version = "0.7.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "itertools"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
@@ -529,6 +630,17 @@ version = "0.2.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "libtest-mimic"
+version = "0.2.0"
+source = "git+https://github.com/LukasKalbertodt/libtest-mimic#bbdd404e13e10f1fae0a2ea6a9fbcc02201bef01"
+dependencies = [
+ "crossbeam-channel 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rayon 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "structopt 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
+ "termcolor 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "log"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -557,11 +669,24 @@ version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "maybe-uninit"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "memchr"
version = "2.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "memoffset"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "mime"
version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -691,14 +816,6 @@ dependencies = [
]
[[package]]
-name = "output_vt100"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
name = "percent-encoding"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -811,14 +928,27 @@ dependencies = [
]
[[package]]
-name = "pretty_assertions"
-version = "0.6.1"
+name = "proc-macro-error"
+version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "ctor 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
- "difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "output_vt100 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro-error-attr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)",
+ "version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "proc-macro-error-attr"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn-mid 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -890,6 +1020,29 @@ dependencies = [
]
[[package]]
+name = "rayon"
+version = "1.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-deque 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rayon-core 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "crossbeam-deque 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-queue 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "redox_syscall"
version = "0.1.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -992,6 +1145,11 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "scopeguard"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "security-framework"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1136,6 +1294,33 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "strsim"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "structopt"
+version = "0.3.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "clap 2.33.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "structopt-derive 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "structopt-derive"
+version = "0.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro-error 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "syn"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1146,6 +1331,16 @@ dependencies = [
]
[[package]]
+name = "syn-mid"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "tempfile"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1159,6 +1354,22 @@ dependencies = [
]
[[package]]
+name = "termcolor"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "textwrap"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "thread_local"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1277,6 +1488,16 @@ dependencies = [
]
[[package]]
+name = "unicode-segmentation"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "unicode-width"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "unicode-xid"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1297,6 +1518,11 @@ version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "vec_map"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "version-sync"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1492,7 +1718,9 @@ dependencies = [
"checksum aho-corasick 0.7.13 (registry+https://github.com/rust-lang/crates.io-index)" = "043164d8ba5c4c3035fec9bbee8647c0261d788f3474306f93bb65901cae0e86"
"checksum annotate-snippets 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aba2d96b8c8b5e656ad7ffb0d09f57772f10a1db74c8d23fca0ec695b38a4047"
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
+"checksum anyhow 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)" = "85bb70cc08ec97ca5450e6eba421deeea5f172c0fc61f78b5357b2a8e8be195f"
"checksum arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"
+"checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
"checksum base64 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e223af0dc48c96d4f8342ec01a4974f139df863896b316681efd36742f22cc67"
"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
@@ -1505,10 +1733,16 @@ dependencies = [
"checksum bytes 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "118cf036fbb97d0816e3c34b2d7a1e8cfc60f68fcf63d550ddbe9bd5f59c213b"
"checksum cc 1.0.54 (registry+https://github.com/rust-lang/crates.io-index)" = "7bbb73db36c1246e9034e307d0fba23f9a2e251faa47ade70c1bd252220c8311"
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
+"checksum clap 2.33.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129"
+"checksum colored-diff 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "516f260afc909bb0d056b76891ad91b3275b83682d851b566792077eee946efd"
"checksum console_error_panic_hook 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b8d976903543e0c48546a91908f21588a680a8c8f984df9a5d69feccb2b2a211"
"checksum core-foundation 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171"
"checksum core-foundation-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac"
-"checksum ctor 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "39858aa5bac06462d4dd4b9164848eb81ffc4aa5c479746393598fd193afa227"
+"checksum crossbeam-channel 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "cced8691919c02aac3cb0a1bc2e9b73d89e832bf9a06fc579d4e71b68a2da061"
+"checksum crossbeam-deque 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285"
+"checksum crossbeam-epoch 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace"
+"checksum crossbeam-queue 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570"
+"checksum crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
"checksum difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
"checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
"checksum digest 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
@@ -1534,6 +1768,7 @@ dependencies = [
"checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb"
"checksum h2 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "79b7246d7e4b979c03fa093da39cfb3617a96bbeee6310af63991668d7e843ff"
"checksum half 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d36fab90f82edc3c747f9d438e06cf0a491055896f2a279638bb5beed6c40177"
+"checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
"checksum hermit-abi 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "b9586eedd4ce6b3c498bc3b4dd92fc9f11166aa908a914071953768066c67909"
"checksum hex 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35"
"checksum http 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "28d569972648b2c512421b5f2a405ad6ac9666547189d0c5477a3f200f3e02f9"
@@ -1544,6 +1779,7 @@ dependencies = [
"checksum idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9"
"checksum indexmap 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c398b2b113b55809ceb9ee3e753fcbac793f1956663f3c36549c1346015c2afe"
"checksum iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e"
+"checksum itertools 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)" = "0d47946d458e94a1b7bcabbf6521ea7c037062c81f534615abcad76e84d4970d"
"checksum itertools 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b"
"checksum itoa 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6"
"checksum js-sys 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)" = "ce10c23ad2ea25ceca0093bd3192229da4c5b3c0f2de499c1ecac0d98d452177"
@@ -1551,11 +1787,14 @@ dependencies = [
"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
"checksum lexical-core 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "db65c6da02e61f55dae90a0ae427b2a5f6b3e8db09f58d10efab23af92592616"
"checksum libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)" = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49"
+"checksum libtest-mimic 0.2.0 (git+https://github.com/LukasKalbertodt/libtest-mimic)" = "<none>"
"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
"checksum loom 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4ecc775857611e1df29abba5c41355cdf540e7e9d4acfdf0f355eefee82330b7"
"checksum maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
+"checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
"checksum memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
+"checksum memoffset 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b4fc2c02a7e374099d4ee95a193111f72d2110197fe200272371758f6c3643d8"
"checksum mime 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
"checksum mime_guess 2.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2684d4c2e97d99848d30b324b00c8fcc7e5c897b7cbb5819b09e7c90e8baf212"
"checksum mio 0.6.22 (registry+https://github.com/rust-lang/crates.io-index)" = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430"
@@ -1569,7 +1808,6 @@ dependencies = [
"checksum openssl 0.10.29 (registry+https://github.com/rust-lang/crates.io-index)" = "cee6d85f4cb4c4f59a6a85d5b68a233d280c82e29e822913b9c8b129fbf20bdd"
"checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de"
"checksum openssl-sys 0.9.58 (registry+https://github.com/rust-lang/crates.io-index)" = "a842db4709b604f0fe5d1170ae3565899be2ad3d9cbc72dedc789ac0511f78de"
-"checksum output_vt100 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53cdc5b785b7a58c5aad8216b3dfa114df64b0b06ae6e1501cef91df2fbdf8f9"
"checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
"checksum pest 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
"checksum pest_consume 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "25f219b98d6adeb806008406459357c7692f413e2dd862219e262858d70a4108"
@@ -1584,7 +1822,8 @@ dependencies = [
"checksum pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677"
"checksum ppv-lite86 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea"
"checksum pretty 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f60c0d9f6fc88ecdd245d90c1920ff76a430ab34303fc778d33b1d0a4c3bf6d3"
-"checksum pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3f81e1644e1b54f5a68959a29aa86cde704219254669da328ecfdf6a1f09d427"
+"checksum proc-macro-error 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "98e9e4b82e0ef281812565ea4751049f1bdcdfccda7d3f459f2e138a40c08678"
+"checksum proc-macro-error-attr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4f5444ead4e9935abd7f27dc51f7e852a0569ac888096d5ec2499470794e2e53"
"checksum proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)" = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4"
"checksum proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)" = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa"
"checksum pulldown-cmark 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3e142c3b8f49d2200605ee6ba0b1d757310e9e7a72afe78c36ee2ef67300ee00"
@@ -1593,6 +1832,8 @@ dependencies = [
"checksum rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
+"checksum rayon 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "62f02856753d04e03e26929f820d0a0a337ebe71f849801eea335d464b349080"
+"checksum rayon-core 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e92e15d89083484e11353891f1af602cc661426deb9564c298b270c726973280"
"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
"checksum regex 1.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6"
"checksum regex-syntax 0.6.18 (registry+https://github.com/rust-lang/crates.io-index)" = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8"
@@ -1604,6 +1845,7 @@ dependencies = [
"checksum schannel 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)" = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75"
"checksum scoped-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "332ffa32bf586782a3efaeb58f127980944bbc8c4d6913a86107ac2a5ab24b28"
"checksum scoped-tls 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2"
+"checksum scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
"checksum security-framework 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "64808902d7d99f78eaddd2b4e2509713babc3dc3c85ad6f4c447680f3c01e535"
"checksum security-framework-sys 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "17bf11d99252f512695eb468de5516e5cf75455521e69dfe343f3b74e4748405"
"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
@@ -1619,8 +1861,14 @@ dependencies = [
"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
"checksum socket2 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)" = "03088793f677dce356f3ccc2edb1b314ad191ab702a5de3faf49304f7e104918"
"checksum static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
+"checksum structopt 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "de2f5e239ee807089b62adce73e48c625e0ed80df02c7ab3f068f5db5281065c"
+"checksum structopt-derive 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "510413f9de616762a4fbeab62509bf15c729603b72d7cd71280fbca431b1c118"
"checksum syn 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)" = "e8d5d96e8cbb005d6959f119f773bfaebb5684296108fb32600c00cde305b2cd"
+"checksum syn-mid 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a"
"checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
+"checksum termcolor 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f"
+"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
"checksum thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
"checksum time 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438"
"checksum tinyvec 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "53953d2d3a5ad81d9f844a32f14ebb121f50b650cd59d0ee2a07cf13c617efed"
@@ -1636,9 +1884,12 @@ dependencies = [
"checksum unicase 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
"checksum unicode-normalization 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6fb19cf769fa8c6a80a162df694621ebeb4dafb606470b2b2fce0be40a98a977"
+"checksum unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"
+"checksum unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479"
"checksum unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
"checksum url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb"
"checksum vcpkg 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "6454029bf181f092ad1b853286f23e2c507d8e8194d01d92da4a55c274a5508c"
+"checksum vec_map 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
"checksum version-sync 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382f6877399646e1b88f4b89813b4577147fa924464317378eb39c280d1e9e4c"
"checksum version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
"checksum walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d"
diff --git a/README.md b/README.md
index 0d140ae..4bd3a92 100644
--- a/README.md
+++ b/README.md
@@ -129,7 +129,7 @@ $ cargo build
```
```bash
-$ cargo test
+$ cargo test -- -q
```
You can also run tests individually by their name:
@@ -169,6 +169,8 @@ same name as the corresponding test.
#### [Unreleased]
+- Fix running tests on Windows. Developing on this lib should now be possible on Windows.
+
#### [0.5.3] - 2020-05-30
- Support building on Windows
diff --git a/dhall/Cargo.toml b/dhall/Cargo.toml
index 9a30d24..94ab80f 100644
--- a/dhall/Cargo.toml
+++ b/dhall/Cargo.toml
@@ -11,6 +11,10 @@ edition = "2018"
build = "build.rs"
include = ["src/**/*", "README.md", "build.rs"]
+[[test]]
+name = "spec"
+harness = false
+
[dependencies]
annotate-snippets = "0.7.0"
hex = "0.4.2"
@@ -31,9 +35,13 @@ url = "2.1"
reqwest = { version = "0.10", features = ["blocking"] }
[dev-dependencies]
-pretty_assertions = "0.6.1"
-version-sync = "0.9"
+anyhow = "1.0.28"
+colored-diff = "0.2.2"
+# Latest master allows tests to be run in parallel.
+libtest-mimic = { version = "0.2.0", git = "https://github.com/LukasKalbertodt/libtest-mimic" }
rand = "0.7"
+version-sync = "0.9"
+walkdir = "2"
[build-dependencies]
walkdir = "2"
diff --git a/dhall/build.rs b/dhall/build.rs
index 660d76a..4fc8545 100644
--- a/dhall/build.rs
+++ b/dhall/build.rs
@@ -1,351 +1,10 @@
use std::env;
-use std::ffi::OsString;
use std::fs::{read_to_string, File};
use std::io::{BufRead, BufReader, Write};
-use std::path::{Path, PathBuf};
-use std::rc::Rc;
-use walkdir::WalkDir;
+use std::path::Path;
use abnf_to_pest::render_rules_to_pest;
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-enum FileType {
- /// Dhall source file
- Text,
- /// Dhall binary file
- Binary,
- /// Text file with hash
- Hash,
- /// Text file with expected text output
- UI,
-}
-
-impl FileType {
- fn to_ext(self) -> &'static str {
- match self {
- FileType::Text => "dhall",
- FileType::Binary => "dhallb",
- FileType::Hash => "hash",
- FileType::UI => "txt",
- }
- }
- fn constructor(self) -> &'static str {
- match self {
- FileType::Text => "TestFile::Source",
- FileType::Binary => "TestFile::Binary",
- FileType::Hash => "TestFile::Binary",
- FileType::UI => "TestFile::UI",
- }
- }
- fn construct(self, path: &str) -> String {
- // e.g. with
- // path = "tests/foor/barA"
- // returns something like:
- // TestFile::Source("tests/foor/barA.dhall")
- format!(r#"{}("{}.{}")"#, self.constructor(), path, self.to_ext())
- }
-}
-
-fn dhall_files_in_dir<'a>(
- dir: &'a Path,
- take_ab_suffix: bool,
- filetype: FileType,
-) -> impl Iterator<Item = (String, String)> + 'a {
- WalkDir::new(dir)
- .into_iter()
- .filter_map(|e| e.ok())
- .filter_map(move |path| {
- let path = path.path().strip_prefix(dir).unwrap();
- let ext = path.extension()?;
- if *ext != OsString::from(filetype.to_ext()) {
- return None;
- }
- let path = path.to_string_lossy();
- let path = &path[..path.len() - 1 - ext.len()];
- let path = if take_ab_suffix && &path[path.len() - 1..] != "A" {
- return None;
- } else if take_ab_suffix {
- path[..path.len() - 1].to_owned()
- } else {
- path.to_owned()
- };
- // Transform path into a valid Rust identifier
- let name = path.replace("/", "_").replace("-", "_");
- Some((name, path))
- })
-}
-
-#[derive(Clone)]
-struct TestFeature {
- /// Name of the module, used in the output of `cargo test`
- module_name: &'static str,
- /// Directory containing the tests files, relative to the base tests directory
- directory: &'static str,
- /// Relevant variant of `dhall::tests::Test`
- variant: &'static str,
- /// Given a file name, whether to only include it in release tests
- too_slow_path: Rc<dyn Fn(&str) -> bool>,
- /// Given a file name, whether to exclude it
- exclude_path: Rc<dyn Fn(&str) -> bool>,
- /// Type of the input file
- input_type: FileType,
- /// Type of the output file, if any
- output_type: Option<FileType>,
-}
-
-fn make_test_module(
- w: &mut impl Write, // Where to output the generated code
- base_paths: &[&Path],
- feature: TestFeature,
-) -> std::io::Result<()> {
- writeln!(w, "mod {} {{", feature.module_name)?;
- let take_ab_suffix = feature.output_type.is_some()
- && (feature.output_type != Some(FileType::UI)
- || feature.module_name == "printer");
- let input_suffix = if take_ab_suffix { "A" } else { "" };
- let output_suffix = if take_ab_suffix { "B" } else { "" };
-
- for base_path in base_paths {
- let tests_dir = base_path.join(feature.directory);
- for (name, path) in
- dhall_files_in_dir(&tests_dir, take_ab_suffix, feature.input_type)
- {
- if (feature.exclude_path)(&path) {
- continue;
- }
- if (feature.too_slow_path)(&path) {
- writeln!(w, "#[cfg(not(debug_assertions))]")?;
- }
- let path = tests_dir.join(path);
- let path = path.to_string_lossy();
-
- let input = feature
- .input_type
- .construct(&format!("{}{}", path, input_suffix));
- let output = match feature.output_type {
- None => None,
- Some(output_type @ FileType::UI) => {
- // All ui outputs are in the local `tests/` directory.
- let path = PathBuf::from("tests/").join(
- PathBuf::from(path.as_ref())
- .strip_prefix(base_path)
- .unwrap(),
- );
- let path = path.to_str().unwrap();
- let output = output_type
- .construct(&format!("{}{}", path, output_suffix));
- Some(output)
- }
- Some(output_type) => {
- let output = output_type
- .construct(&format!("{}{}", path, output_suffix));
- Some(output)
- }
- };
-
- let test = match output {
- None => format!("{}({})", feature.variant, input),
- Some(output) => {
- format!("{}({}, {})", feature.variant, input, output)
- }
- };
- writeln!(w, "make_spec_test!({}, {});", test, name)?;
- }
- }
- writeln!(w, "}}")?;
- Ok(())
-}
-
-fn generate_tests() -> std::io::Result<()> {
- // To force regeneration of the test list, `touch dhall/build.rs`
- let out_dir = env::var("OUT_DIR").unwrap();
-
- let parser_tests_path = Path::new(&out_dir).join("spec_tests.rs");
- let spec_tests_dirs =
- vec![Path::new("../dhall-lang/tests/"), Path::new("tests/")];
-
- let default_feature = TestFeature {
- module_name: "",
- directory: "",
- variant: "",
- too_slow_path: Rc::new(|_path: &str| false),
- exclude_path: Rc::new(|_path: &str| false),
- input_type: FileType::Text,
- output_type: None,
- };
-
- #[allow(clippy::nonminimal_bool)]
- let tests = vec![
- TestFeature {
- module_name: "parser_success",
- directory: "parser/success/",
- variant: "ParserSuccess",
- too_slow_path: Rc::new(|path: &str| path == "largeExpression"),
- exclude_path: Rc::new(|path: &str| {
- false
- // Pretty sure the test is incorrect
- || path == "unit/import/urls/quotedPathFakeUrlEncode"
- }),
- output_type: Some(FileType::Binary),
- ..default_feature
- },
- TestFeature {
- module_name: "parser_failure",
- directory: "parser/failure/",
- variant: "ParserFailure",
- output_type: Some(FileType::UI),
- ..default_feature.clone()
- },
- TestFeature {
- module_name: "printer",
- directory: "parser/success/",
- variant: "Printer",
- too_slow_path: Rc::new(|path: &str| path == "largeExpression"),
- output_type: Some(FileType::UI),
- ..default_feature.clone()
- },
- TestFeature {
- module_name: "binary_encoding",
- directory: "parser/success/",
- variant: "BinaryEncoding",
- too_slow_path: Rc::new(|path: &str| path == "largeExpression"),
- exclude_path: Rc::new(|path: &str| {
- false
- // Pretty sure the test is incorrect
- || path == "unit/import/urls/quotedPathFakeUrlEncode"
- // See https://github.com/pyfisch/cbor/issues/109
- || path == "double"
- || path == "unit/DoubleLitExponentNoDot"
- || path == "unit/DoubleLitSecretelyInt"
- }),
- output_type: Some(FileType::Binary),
- ..default_feature
- },
- TestFeature {
- module_name: "binary_decoding_success",
- directory: "binary-decode/success/",
- variant: "BinaryDecodingSuccess",
- exclude_path: Rc::new(|path: &str| {
- false
- // We don't support bignums
- || path == "unit/IntegerBigNegative"
- || path == "unit/IntegerBigPositive"
- || path == "unit/NaturalBig"
- }),
- input_type: FileType::Binary,
- output_type: Some(FileType::Text),
- ..default_feature.clone()
- },
- TestFeature {
- module_name: "binary_decoding_failure",
- directory: "binary-decode/failure/",
- variant: "BinaryDecodingFailure",
- input_type: FileType::Binary,
- output_type: Some(FileType::UI),
- ..default_feature.clone()
- },
- TestFeature {
- module_name: "import_success",
- directory: "import/success/",
- variant: "ImportSuccess",
- exclude_path: Rc::new(|path: &str| {
- false
- // TODO: the standard does not respect https://tools.ietf.org/html/rfc3986#section-5.2
- || path == "unit/asLocation/RemoteCanonicalize4"
- // TODO: import headers
- || path == "customHeaders"
- || path == "headerForwarding"
- || path == "noHeaderForwarding"
- }),
- output_type: Some(FileType::Text),
- ..default_feature.clone()
- },
- TestFeature {
- module_name: "import_failure",
- directory: "import/failure/",
- variant: "ImportFailure",
- exclude_path: Rc::new(|path: &str| {
- false
- // TODO: import headers
- || path == "customHeadersUsingBoundVariable"
- }),
- output_type: Some(FileType::UI),
- ..default_feature.clone()
- },
- TestFeature {
- module_name: "semantic_hash",
- directory: "semantic-hash/success/",
- variant: "SemanticHash",
- exclude_path: Rc::new(|path: &str| {
- false
- // We don't support bignums
- || path == "simple/integerToDouble"
- // See https://github.com/pyfisch/cbor/issues/109
- || path == "prelude/Integer/toDouble/0"
- || path == "prelude/Integer/toDouble/1"
- || path == "prelude/Natural/toDouble/0"
- }),
- output_type: Some(FileType::Hash),
- ..default_feature.clone()
- },
- TestFeature {
- module_name: "beta_normalize",
- directory: "normalization/success/",
- variant: "Normalization",
- too_slow_path: Rc::new(|path: &str| path == "remoteSystems"),
- exclude_path: Rc::new(|path: &str| {
- false
- // Cannot typecheck
- || path == "unit/Sort"
- // We don't support bignums
- || path == "simple/integerToDouble"
- // TODO: fix Double/show
- || path == "prelude/JSON/number/1"
- }),
- output_type: Some(FileType::Text),
- ..default_feature
- },
- TestFeature {
- module_name: "alpha_normalize",
- directory: "alpha-normalization/success/",
- variant: "AlphaNormalization",
- exclude_path: Rc::new(|path: &str| {
- // This test is designed to not typecheck
- path == "unit/FunctionNestedBindingXXFree"
- }),
- output_type: Some(FileType::Text),
- ..default_feature.clone()
- },
- TestFeature {
- module_name: "type_inference_success",
- directory: "type-inference/success/",
- variant: "TypeInferenceSuccess",
- too_slow_path: Rc::new(|path: &str| path == "prelude"),
- output_type: Some(FileType::Text),
- ..default_feature.clone()
- },
- TestFeature {
- module_name: "type_inference_failure",
- directory: "type-inference/failure/",
- variant: "TypeInferenceFailure",
- exclude_path: Rc::new(|path: &str| {
- false
- // TODO: enable free variable checking
- || path == "unit/MergeHandlerFreeVar"
- }),
- output_type: Some(FileType::UI),
- ..default_feature
- },
- ];
-
- let mut file = File::create(parser_tests_path)?;
- for test in tests {
- make_test_module(&mut file, &spec_tests_dirs, test)?;
- }
-
- Ok(())
-}
-
fn convert_abnf_to_pest() -> std::io::Result<()> {
let out_dir = env::var("OUT_DIR").unwrap();
let abnf_path = "src/syntax/text/dhall.abnf";
@@ -476,6 +135,5 @@ fn generate_pest_parser() -> std::io::Result<()> {
fn main() -> std::io::Result<()> {
convert_abnf_to_pest()?;
generate_pest_parser()?;
- generate_tests()?;
Ok(())
}
diff --git a/dhall/src/error/mod.rs b/dhall/src/error/mod.rs
index 0cfa93c..d533264 100644
--- a/dhall/src/error/mod.rs
+++ b/dhall/src/error/mod.rs
@@ -92,6 +92,17 @@ impl std::fmt::Display for TypeError {
impl std::error::Error for TypeError {}
+impl std::fmt::Display for EncodeError {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ let msg = match self {
+ EncodeError::CBORError(e) => format!("Encode error: {}", e),
+ };
+ write!(f, "{}", msg)
+ }
+}
+
+impl std::error::Error for EncodeError {}
+
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match &self.kind {
diff --git a/dhall/src/lib.rs b/dhall/src/lib.rs
index 73f0b74..bda31d5 100644
--- a/dhall/src/lib.rs
+++ b/dhall/src/lib.rs
@@ -8,8 +8,6 @@
clippy::useless_format
)]
-mod tests;
-
pub mod builtins;
pub mod error;
pub mod operations;
diff --git a/dhall/src/tests.rs b/dhall/src/tests.rs
deleted file mode 100644
index 08a4a4a..0000000
--- a/dhall/src/tests.rs
+++ /dev/null
@@ -1,360 +0,0 @@
-#[cfg(not(test))]
-use assert_eq as assert_eq_pretty;
-#[cfg(test)]
-use pretty_assertions::assert_eq as assert_eq_pretty;
-
-use std::env;
-use std::fmt::Display;
-use std::fs::{create_dir_all, read_to_string, File};
-use std::io::{Read, Write};
-use std::path::PathBuf;
-
-use crate::error::{ErrorKind, Result};
-use crate::syntax::{binary, Expr};
-use crate::{Normalized, Parsed, Resolved, Typed};
-
-macro_rules! assert_eq_display {
- ($left:expr, $right:expr) => {{
- match (&$left, &$right) {
- (left_val, right_val) => {
- if !(*left_val == *right_val) {
- panic!(
- r#"assertion failed: `(left == right)`
- left: `{}`,
-right: `{}`"#,
- left_val, right_val
- )
- }
- }
- }
- }};
-}
-
-/// Wrapper around string slice that makes debug output `{:?}` to print string same way as `{}`.
-/// Used in different `assert*!` macros in combination with `pretty_assertions` crate to make
-/// test failures to show nice diffs.
-#[derive(PartialEq, Eq)]
-#[doc(hidden)]
-pub struct PrettyString(String);
-
-/// Make diff to display string as multi-line string
-impl std::fmt::Debug for PrettyString {
- fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
- f.write_str(&self.0)
- }
-}
-
-macro_rules! assert_eq_pretty_str {
- ($left:expr, $right:expr) => {
- assert_eq_pretty!(
- PrettyString($left.to_string()),
- PrettyString($right.to_string())
- );
- };
-}
-
-#[allow(dead_code)]
-enum Test {
- ParserSuccess(TestFile, TestFile),
- ParserFailure(TestFile, TestFile),
- Printer(TestFile, TestFile),
- BinaryEncoding(TestFile, TestFile),
- BinaryDecodingSuccess(TestFile, TestFile),
- BinaryDecodingFailure(TestFile, TestFile),
- ImportSuccess(TestFile, TestFile),
- ImportFailure(TestFile, TestFile),
- SemanticHash(TestFile, TestFile),
- TypeInferenceSuccess(TestFile, TestFile),
- TypeInferenceFailure(TestFile, TestFile),
- Normalization(TestFile, TestFile),
- AlphaNormalization(TestFile, TestFile),
-}
-
-#[allow(dead_code)]
-enum TestFile {
- Source(&'static str),
- Binary(&'static str),
- UI(&'static str),
-}
-
-impl TestFile {
- pub fn path(&self) -> PathBuf {
- match self {
- TestFile::Source(path)
- | TestFile::Binary(path)
- | TestFile::UI(path) => PathBuf::from("dhall").join(path),
- }
- }
-
- /// Parse the target file
- pub fn parse(&self) -> Result<Parsed> {
- match self {
- TestFile::Source(_) => Parsed::parse_file(&self.path()),
- TestFile::Binary(_) => Parsed::parse_binary_file(&self.path()),
- TestFile::UI(_) => panic!("Can't parse a UI test file"),
- }
- }
- /// Parse and resolve the target file
- pub fn resolve(&self) -> Result<Resolved> {
- Ok(self.parse()?.resolve()?)
- }
- /// Parse, resolve and tck the target file
- pub fn typecheck(&self) -> Result<Typed> {
- Ok(self.resolve()?.typecheck()?)
- }
- /// Parse, resolve, tck and normalize the target file
- pub fn normalize(&self) -> Result<Normalized> {
- Ok(self.typecheck()?.normalize())
- }
-
- /// If UPDATE_TEST_FILES=1, we overwrite the output files with our own output.
- fn force_update() -> bool {
- env::var("UPDATE_TEST_FILES") == Ok("1".to_string())
- }
- /// Write the provided expression to the pointed file.
- fn write_expr(&self, expr: impl Into<Expr>) -> Result<()> {
- let expr = expr.into();
- let path = self.path();
- create_dir_all(path.parent().unwrap())?;
- let mut file = File::create(path)?;
- match self {
- TestFile::Source(_) => {
- writeln!(file, "{}", expr)?;
- }
- TestFile::Binary(_) => {
- let expr_data = binary::encode(&expr)?;
- file.write_all(&expr_data)?;
- }
- TestFile::UI(_) => panic!("Can't write an expression to a UI file"),
- }
- Ok(())
- }
- /// Write the provided string to the pointed file.
- fn write_ui(&self, x: impl Display) -> Result<()> {
- match self {
- TestFile::UI(_) => {}
- _ => panic!("Can't write a ui string to a dhall file"),
- }
- let path = self.path();
- create_dir_all(path.parent().unwrap())?;
- let mut file = File::create(path)?;
- writeln!(file, "{}", x)?;
- Ok(())
- }
-
- /// Check that the provided expression matches the file contents.
- pub fn compare(&self, expr: impl Into<Expr>) -> Result<()> {
- let expr = expr.into();
- if !self.path().is_file() {
- return self.write_expr(expr);
- }
-
- let expected = self.parse()?.to_expr();
- if expr != expected {
- if Self::force_update() {
- self.write_expr(expr)?;
- } else {
- assert_eq_display!(expr, expected);
- }
- }
- Ok(())
- }
- /// Check that the provided expression matches the file contents.
- pub fn compare_debug(&self, expr: impl Into<Expr>) -> Result<()> {
- let expr = expr.into();
- if !self.path().is_file() {
- return self.write_expr(expr);
- }
-
- let expected = self.parse()?.to_expr();
- if expr != expected {
- if Self::force_update() {
- self.write_expr(expr)?;
- } else {
- assert_eq_pretty!(expr, expected);
- }
- }
- Ok(())
- }
- /// Check that the provided expression matches the file contents.
- pub fn compare_binary(&self, expr: impl Into<Expr>) -> Result<()> {
- let expr = expr.into();
- match self {
- TestFile::Binary(_) => {}
- _ => panic!("This is not a binary file"),
- }
- if !self.path().is_file() {
- return self.write_expr(expr);
- }
-
- let expr_data = binary::encode(&expr)?;
- let expected_data = {
- let mut data = Vec::new();
- File::open(&self.path())?.read_to_end(&mut data)?;
- data
- };
-
- // Compare bit-by-bit
- if expr_data != expected_data {
- if Self::force_update() {
- self.write_expr(expr)?;
- } else {
- use serde_cbor::de::from_slice;
- use serde_cbor::value::Value;
- // Pretty-print difference
- assert_eq_pretty!(
- from_slice::<Value>(&expr_data).unwrap(),
- from_slice::<Value>(&expected_data).unwrap()
- );
- // If difference was not visible in the cbor::Nir, compare normally.
- assert_eq!(expr_data, expected_data);
- }
- }
- Ok(())
- }
- /// Check that the provided string matches the file contents. Writes to the corresponding file
- /// if it is missing.
- pub fn compare_ui(&self, x: impl Display) -> Result<()> {
- if !self.path().is_file() {
- return self.write_ui(x);
- }
-
- let expected = read_to_string(self.path())?;
- let msg = format!("{}\n", x);
- if msg != expected {
- if Self::force_update() {
- self.write_ui(x)?;
- } else {
- assert_eq_pretty_str!(expected, msg);
- }
- }
- Ok(())
- }
-}
-
-#[allow(dead_code)]
-fn run_test_or_panic(test: Test) {
- let res = if env::var("CI_GRCOV").is_ok() {
- // Augment stack size when running with 0 inlining to avoid overflows
- std::thread::Builder::new()
- .stack_size(128 * 1024 * 1024)
- .spawn(|| run_test(test))
- .unwrap()
- .join()
- .unwrap()
- } else {
- run_test(test)
- };
- match res {
- Ok(_) => {}
- Err(e) => panic!(e.to_string()),
- }
-}
-
-fn run_test(test: Test) -> Result<()> {
- use self::Test::*;
- // Setup current directory to the root of the repository. Important for `as Location` tests.
- let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
- .parent()
- .unwrap()
- .to_path_buf();
- env::set_current_dir(root_dir.as_path())?;
- // Set environment variable for import tests.
- env::set_var("DHALL_TEST_VAR", "6 * 7");
-
- match test {
- ParserSuccess(expr, expected) => {
- let expr = expr.parse()?;
- // This exercices both parsing and binary decoding
- expected.compare_debug(expr)?;
- }
- ParserFailure(expr, expected) => {
- use std::io;
- let err = expr.parse().unwrap_err();
- match err.kind() {
- ErrorKind::Parse(_) => {}
- ErrorKind::IO(e) if e.kind() == io::ErrorKind::InvalidData => {}
- e => panic!("Expected parse error, got: {:?}", e),
- }
- expected.compare_ui(err)?;
- }
- BinaryEncoding(expr, expected) => {
- let expr = expr.parse()?;
- expected.compare_binary(expr)?;
- }
- BinaryDecodingSuccess(expr, expected) => {
- let expr = expr.parse()?;
- expected.compare_debug(expr)?;
- }
- BinaryDecodingFailure(expr, expected) => {
- let err = expr.parse().unwrap_err();
- expected.compare_ui(err)?;
- }
- Printer(expr, expected) => {
- let parsed = expr.parse()?;
- // Round-trip pretty-printer
- let reparsed = Parsed::parse_str(&parsed.to_string())?;
- assert_eq!(reparsed, parsed);
- expected.compare_ui(parsed)?;
- }
- ImportSuccess(expr, expected) => {
- // Configure cache for import tests
- env::set_var(
- "XDG_CACHE_HOME",
- root_dir
- .join("dhall-lang")
- .join("tests")
- .join("import")
- .join("cache")
- .as_path(),
- );
- let expr = expr.normalize()?;
- expected.compare(expr)?;
- }
- ImportFailure(expr, expected) => {
- let err = expr.resolve().unwrap_err();
- expected.compare_ui(err)?;
- }
- SemanticHash(expr, expected) => {
- let expr = expr.normalize()?.to_expr_alpha();
- let hash = hex::encode(expr.hash()?);
- expected.compare_ui(format!("sha256:{}", hash))?;
- }
- TypeInferenceSuccess(expr, expected) => {
- let ty = expr.typecheck()?.get_type()?;
- expected.compare(ty)?;
- }
- TypeInferenceFailure(expr, expected) => {
- let err = expr.typecheck().unwrap_err();
- expected.compare_ui(err)?;
- }
- Normalization(expr, expected) => {
- let expr = expr.normalize()?;
- expected.compare(expr)?;
- }
- AlphaNormalization(expr, expected) => {
- let expr = expr.normalize()?.to_expr_alpha();
- expected.compare(expr)?;
- }
- }
-
- Ok(())
-}
-
-#[cfg(test)]
-mod spec {
- macro_rules! make_spec_test {
- ($type:expr, $name:ident) => {
- #[test]
- #[allow(non_snake_case)]
- fn $name() {
- use crate::tests::Test::*;
- use crate::tests::*;
- run_test_or_panic($type);
- }
- };
- }
-
- // See build.rs
- include!(concat!(env!("OUT_DIR"), "/spec_tests.rs"));
-}
diff --git a/dhall/tests/spec.rs b/dhall/tests/spec.rs
new file mode 100644
index 0000000..a423435
--- /dev/null
+++ b/dhall/tests/spec.rs
@@ -0,0 +1,713 @@
+use anyhow::Result;
+use std::env;
+use std::ffi::OsString;
+use std::fmt::{Debug, Display};
+use std::fs::{create_dir_all, read_to_string, File};
+use std::io::{Read, Write};
+use std::path::{Path, PathBuf};
+use std::rc::Rc;
+
+use libtest_mimic::{Arguments, Outcome, Test};
+use walkdir::WalkDir;
+
+use dhall::error::Error as DhallError;
+use dhall::error::ErrorKind;
+use dhall::syntax::{binary, Expr};
+use dhall::{Normalized, Parsed, Resolved, Typed};
+
+static LOCAL_TEST_PATH: &str = "tests/";
+static TEST_PATHS: &[&str] = &["../dhall-lang/tests/", LOCAL_TEST_PATH];
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+enum FileType {
+ /// Dhall source file
+ Text,
+ /// Dhall binary file
+ Binary,
+ /// Text file with hash
+ Hash,
+ /// Text file with expected text output
+ UI,
+}
+
+#[derive(Clone)]
+enum TestFile {
+ Source(String),
+ Binary(String),
+ UI(String),
+}
+
+impl FileType {
+ fn to_ext(self) -> &'static str {
+ match self {
+ FileType::Text => "dhall",
+ FileType::Binary => "dhallb",
+ FileType::Hash => "hash",
+ FileType::UI => "txt",
+ }
+ }
+ fn construct(self, path: &str) -> TestFile {
+ let file = format!("{}.{}", path, self.to_ext());
+ match self {
+ FileType::Text => TestFile::Source(file),
+ FileType::Binary => TestFile::Binary(file),
+ FileType::Hash => TestFile::Binary(file),
+ FileType::UI => TestFile::UI(file),
+ }
+ }
+}
+
+// Custom assert_eq macro that returns an Error and prints pretty diffs.
+macro_rules! assert_eq {
+ (@@make_str, debug, $x:expr) => {
+ format!("{:#?}", $x)
+ };
+ (@@make_str, display, $x:expr) => {
+ $x.to_string()
+ };
+
+ (@$style:ident, $left:expr, $right:expr) => {
+ match (&$left, &$right) {
+ (left_val, right_val) => {
+ if *left_val != *right_val {
+ let left_val = assert_eq!(@@make_str, $style, left_val);
+ let right_val = assert_eq!(@@make_str, $style, right_val);
+ let msg = format!(
+ "assertion failed: `(left == right)`\n\n{}\n",
+ colored_diff::PrettyDifference {
+ expected: &left_val,
+ actual: &right_val
+ }
+ );
+ return Err(TestError(msg).into());
+ }
+ }
+ }
+ };
+ ($left:expr, $right:expr) => {
+ assert_eq!(@debug, $left, $right)
+ };
+}
+
+impl TestFile {
+ pub fn path(&self) -> PathBuf {
+ match self {
+ TestFile::Source(path)
+ | TestFile::Binary(path)
+ | TestFile::UI(path) => PathBuf::from("dhall").join(path),
+ }
+ }
+
+ /// Parse the target file
+ pub fn parse(&self) -> Result<Parsed> {
+ Ok(match self {
+ TestFile::Source(_) => Parsed::parse_file(&self.path())?,
+ TestFile::Binary(_) => Parsed::parse_binary_file(&self.path())?,
+ TestFile::UI(_) => {
+ Err(TestError(format!("Can't parse a UI test file")))?
+ }
+ })
+ }
+ /// Parse and resolve the target file
+ pub fn resolve(&self) -> Result<Resolved> {
+ Ok(self.parse()?.resolve()?)
+ }
+ /// Parse, resolve and tck the target file
+ pub fn typecheck(&self) -> Result<Typed> {
+ Ok(self.resolve()?.typecheck()?)
+ }
+ /// Parse, resolve, tck and normalize the target file
+ pub fn normalize(&self) -> Result<Normalized> {
+ Ok(self.typecheck()?.normalize())
+ }
+
+ /// If UPDATE_TEST_FILES=1, we overwrite the output files with our own output.
+ fn force_update() -> bool {
+ env::var("UPDATE_TEST_FILES") == Ok("1".to_string())
+ }
+ /// Write the provided expression to the pointed file.
+ fn write_expr(&self, expr: impl Into<Expr>) -> Result<()> {
+ let expr = expr.into();
+ let path = self.path();
+ create_dir_all(path.parent().unwrap())?;
+ let mut file = File::create(path)?;
+ match self {
+ TestFile::Source(_) => {
+ writeln!(file, "{}", expr)?;
+ }
+ TestFile::Binary(_) => {
+ let expr_data = binary::encode(&expr)?;
+ file.write_all(&expr_data)?;
+ }
+ TestFile::UI(_) => Err(TestError(format!(
+ "Can't write an expression to a UI file"
+ )))?,
+ }
+ Ok(())
+ }
+ /// Write the provided string to the pointed file.
+ fn write_ui(&self, x: impl Display) -> Result<()> {
+ match self {
+ TestFile::UI(_) => {}
+ _ => Err(TestError(format!(
+ "Can't write a ui string to a dhall file"
+ )))?,
+ }
+ let path = self.path();
+ create_dir_all(path.parent().unwrap())?;
+ let mut file = File::create(path)?;
+ writeln!(file, "{}", x)?;
+ Ok(())
+ }
+
+ /// Check that the provided expression matches the file contents.
+ pub fn compare(&self, expr: impl Into<Expr>) -> Result<()> {
+ let expr = expr.into();
+ if !self.path().is_file() {
+ return self.write_expr(expr);
+ }
+
+ let expected = self.parse()?.to_expr();
+ if expr != expected {
+ if Self::force_update() {
+ self.write_expr(expr)?;
+ } else {
+ assert_eq!(@display, expr, expected);
+ }
+ }
+ Ok(())
+ }
+ /// Check that the provided expression matches the file contents.
+ pub fn compare_debug(&self, expr: impl Into<Expr>) -> Result<()> {
+ let expr = expr.into();
+ if !self.path().is_file() {
+ return self.write_expr(expr);
+ }
+
+ let expected = self.parse()?.to_expr();
+ if expr != expected {
+ if Self::force_update() {
+ self.write_expr(expr)?;
+ } else {
+ assert_eq!(expr, expected);
+ }
+ }
+ Ok(())
+ }
+ /// Check that the provided expression matches the file contents.
+ pub fn compare_binary(&self, expr: impl Into<Expr>) -> Result<()> {
+ let expr = expr.into();
+ match self {
+ TestFile::Binary(_) => {}
+ _ => Err(TestError(format!("This is not a binary file")))?,
+ }
+ if !self.path().is_file() {
+ return self.write_expr(expr);
+ }
+
+ let expr_data = binary::encode(&expr)?;
+ let expected_data = {
+ let mut data = Vec::new();
+ File::open(&self.path())?.read_to_end(&mut data)?;
+ data
+ };
+
+ // Compare bit-by-bit
+ if expr_data != expected_data {
+ if Self::force_update() {
+ self.write_expr(expr)?;
+ } else {
+ use serde_cbor::de::from_slice;
+ use serde_cbor::value::Value;
+ // Pretty-print difference
+ assert_eq!(
+ from_slice::<Value>(&expr_data).unwrap(),
+ from_slice::<Value>(&expected_data).unwrap()
+ );
+ // If difference was not visible in the cbor::Nir, compare normally.
+ assert_eq!(expr_data, expected_data);
+ }
+ }
+ Ok(())
+ }
+ /// Check that the provided string matches the file contents. Writes to the corresponding file
+ /// if it is missing.
+ pub fn compare_ui(&self, x: impl Display) -> Result<()> {
+ if !self.path().is_file() {
+ return self.write_ui(x);
+ }
+
+ let expected = read_to_string(self.path())?;
+ let expected = expected.replace("\r\n", "\n"); // Normalize line endings
+ let msg = format!("{}\n", x);
+ // TODO: git changes newlines on windows
+ let msg = msg.replace("\r\n", "\n");
+ if msg != expected {
+ if Self::force_update() {
+ self.write_ui(x)?;
+ } else {
+ assert_eq!(@display, expected, msg);
+ }
+ }
+ Ok(())
+ }
+}
+
+#[derive(Clone)]
+struct TestFeature {
+ /// Name of the module, used in the output of `cargo test`
+ module_name: &'static str,
+ /// Directory containing the tests files, relative to the base tests directory
+ directory: &'static str,
+ /// Relevant variant of `dhall::tests::SpecTestKind`
+ variant: SpecTestKind,
+ /// Given a file name, whether to only include it in release tests
+ too_slow_path: Rc<dyn Fn(&str) -> bool>,
+ /// Given a file name, whether to exclude it
+ exclude_path: Rc<dyn Fn(&str) -> bool>,
+ /// Type of the input file
+ input_type: FileType,
+ /// Type of the output file
+ output_type: FileType,
+}
+
+#[derive(Clone, Copy)]
+enum SpecTestKind {
+ ParserSuccess,
+ ParserFailure,
+ Printer,
+ BinaryEncoding,
+ BinaryDecodingSuccess,
+ BinaryDecodingFailure,
+ ImportSuccess,
+ ImportFailure,
+ SemanticHash,
+ TypeInferenceSuccess,
+ TypeInferenceFailure,
+ Normalization,
+ AlphaNormalization,
+}
+
+#[derive(Clone)]
+struct SpecTest {
+ kind: SpecTestKind,
+ input: TestFile,
+ output: TestFile,
+}
+
+#[derive(Debug, Clone)]
+struct TestError(String);
+
+impl std::fmt::Display for TestError {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ write!(f, "{}", &self.0)
+ }
+}
+impl std::error::Error for TestError {}
+
+fn dhall_files_in_dir<'a>(
+ dir: &'a Path,
+ take_ab_suffix: bool,
+ filetype: FileType,
+) -> impl Iterator<Item = String> + 'a {
+ WalkDir::new(dir)
+ .into_iter()
+ .filter_map(|e| e.ok())
+ .filter_map(move |path| {
+ let path = path.path().strip_prefix(dir).unwrap();
+ let ext = path.extension()?;
+ if *ext != OsString::from(filetype.to_ext()) {
+ return None;
+ }
+ let path = path.to_string_lossy();
+ let path = &path[..path.len() - 1 - ext.len()];
+ let path = if take_ab_suffix && &path[path.len() - 1..] != "A" {
+ return None;
+ } else if take_ab_suffix {
+ path[..path.len() - 1].to_owned()
+ } else {
+ path.to_owned()
+ };
+ Some(path)
+ })
+}
+
+fn discover_tests_for_feature(feature: TestFeature) -> Vec<Test<SpecTest>> {
+ let take_ab_suffix =
+ feature.output_type != FileType::UI || feature.module_name == "printer";
+ let input_suffix = if take_ab_suffix { "A" } else { "" };
+ let output_suffix = if take_ab_suffix { "B" } else { "" };
+
+ let mut tests = Vec::new();
+ for base_path in TEST_PATHS {
+ let base_path = Path::new(base_path);
+ let tests_dir = base_path.join(feature.directory);
+ for path in
+ dhall_files_in_dir(&tests_dir, take_ab_suffix, feature.input_type)
+ {
+ let normalized_path = path.replace("\\", "/");
+ if (feature.exclude_path)(&normalized_path) {
+ continue;
+ }
+ if (feature.too_slow_path)(&normalized_path)
+ && cfg!(debug_assertions)
+ {
+ continue;
+ }
+ let path = tests_dir.join(path);
+ let path = path.to_string_lossy();
+
+ let output_path = match feature.output_type {
+ FileType::UI => {
+ // All ui outputs are in the local tests directory.
+ let path = PathBuf::from(LOCAL_TEST_PATH).join(
+ PathBuf::from(path.as_ref())
+ .strip_prefix(base_path)
+ .unwrap(),
+ );
+ path.to_str().unwrap().to_owned()
+ }
+ _ => path.as_ref().to_owned(),
+ };
+
+ // Transform path into a valid Rust identifier
+ let name = normalized_path.replace("/", "_").replace("-", "_");
+ let input = feature
+ .input_type
+ .construct(&format!("{}{}", path, input_suffix));
+ let output = feature
+ .output_type
+ .construct(&format!("{}{}", output_path, output_suffix));
+
+ tests.push(Test {
+ name: format!("{}::{}", feature.module_name, name),
+ kind: "".into(),
+ is_ignored: false,
+ is_bench: false,
+ data: SpecTest {
+ input,
+ output,
+ kind: feature.variant,
+ },
+ });
+ }
+ }
+ tests
+}
+
+fn define_features() -> Vec<TestFeature> {
+ let default_feature = TestFeature {
+ module_name: "",
+ directory: "",
+ variant: SpecTestKind::ParserSuccess, // Dummy
+ too_slow_path: Rc::new(|_path: &str| false),
+ exclude_path: Rc::new(|_path: &str| false),
+ input_type: FileType::Text,
+ output_type: FileType::Text,
+ };
+
+ #[allow(clippy::nonminimal_bool)]
+ vec![
+ TestFeature {
+ module_name: "parser_success",
+ directory: "parser/success/",
+ variant: SpecTestKind::ParserSuccess,
+ too_slow_path: Rc::new(|path: &str| path == "largeExpression"),
+ exclude_path: Rc::new(|path: &str| {
+ false
+ // Pretty sure the test is incorrect
+ || path == "unit/import/urls/quotedPathFakeUrlEncode"
+ }),
+ output_type: FileType::Binary,
+ ..default_feature.clone()
+ },
+ TestFeature {
+ module_name: "parser_failure",
+ directory: "parser/failure/",
+ variant: SpecTestKind::ParserFailure,
+ output_type: FileType::UI,
+ exclude_path: Rc::new(|_path: &str| {
+ // TODO: git changes newlines on windows
+ cfg!(windows)
+ }),
+ ..default_feature.clone()
+ },
+ TestFeature {
+ module_name: "printer",
+ directory: "parser/success/",
+ variant: SpecTestKind::Printer,
+ too_slow_path: Rc::new(|path: &str| path == "largeExpression"),
+ output_type: FileType::UI,
+ ..default_feature.clone()
+ },
+ TestFeature {
+ module_name: "binary_encoding",
+ directory: "parser/success/",
+ variant: SpecTestKind::BinaryEncoding,
+ too_slow_path: Rc::new(|path: &str| path == "largeExpression"),
+ exclude_path: Rc::new(|path: &str| {
+ false
+ // Pretty sure the test is incorrect
+ || path == "unit/import/urls/quotedPathFakeUrlEncode"
+ // See https://github.com/pyfisch/cbor/issues/109
+ || path == "double"
+ || path == "unit/DoubleLitExponentNoDot"
+ || path == "unit/DoubleLitSecretelyInt"
+ }),
+ output_type: FileType::Binary,
+ ..default_feature.clone()
+ },
+ TestFeature {
+ module_name: "binary_decoding_success",
+ directory: "binary-decode/success/",
+ variant: SpecTestKind::BinaryDecodingSuccess,
+ exclude_path: Rc::new(|path: &str| {
+ false
+ // We don't support bignums
+ || path == "unit/IntegerBigNegative"
+ || path == "unit/IntegerBigPositive"
+ || path == "unit/NaturalBig"
+ }),
+ input_type: FileType::Binary,
+ ..default_feature.clone()
+ },
+ TestFeature {
+ module_name: "binary_decoding_failure",
+ directory: "binary-decode/failure/",
+ variant: SpecTestKind::BinaryDecodingFailure,
+ input_type: FileType::Binary,
+ output_type: FileType::UI,
+ ..default_feature.clone()
+ },
+ TestFeature {
+ module_name: "import_success",
+ directory: "import/success/",
+ variant: SpecTestKind::ImportSuccess,
+ exclude_path: Rc::new(|path: &str| {
+ false
+ // TODO: the standard does not respect https://tools.ietf.org/html/rfc3986#section-5.2
+ || path == "unit/asLocation/RemoteCanonicalize4"
+ // TODO: import headers
+ || path == "customHeaders"
+ || path == "headerForwarding"
+ || path == "noHeaderForwarding"
+ // TODO: git changes newlines on windows
+ || (cfg!(windows) && path == "unit/AsText")
+ // TODO: paths on windows have backslashes; this breaks all the `as Location` tests
+ // See https://github.com/dhall-lang/dhall-lang/issues/1032
+ || (cfg!(windows) && path.contains("asLocation"))
+ }),
+ ..default_feature.clone()
+ },
+ TestFeature {
+ module_name: "import_failure",
+ directory: "import/failure/",
+ variant: SpecTestKind::ImportFailure,
+ exclude_path: Rc::new(|path: &str| {
+ false
+ // TODO: paths on windows have backslashes; this breaks many things
+ || cfg!(windows)
+ // TODO: import headers
+ || path == "customHeadersUsingBoundVariable"
+ }),
+ output_type: FileType::UI,
+ ..default_feature.clone()
+ },
+ TestFeature {
+ module_name: "semantic_hash",
+ directory: "semantic-hash/success/",
+ variant: SpecTestKind::SemanticHash,
+ exclude_path: Rc::new(|path: &str| {
+ false
+ // We don't support bignums
+ || path == "simple/integerToDouble"
+ // See https://github.com/pyfisch/cbor/issues/109
+ || path == "prelude/Integer/toDouble/0"
+ || path == "prelude/Integer/toDouble/1"
+ || path == "prelude/Natural/toDouble/0"
+ }),
+ output_type: FileType::Hash,
+ ..default_feature.clone()
+ },
+ TestFeature {
+ module_name: "beta_normalize",
+ directory: "normalization/success/",
+ variant: SpecTestKind::Normalization,
+ too_slow_path: Rc::new(|path: &str| path == "remoteSystems"),
+ exclude_path: Rc::new(|path: &str| {
+ false
+ // Cannot typecheck
+ || path == "unit/Sort"
+ // We don't support bignums
+ || path == "simple/integerToDouble"
+ // TODO: fix Double/show
+ || path == "prelude/JSON/number/1"
+ }),
+ ..default_feature.clone()
+ },
+ TestFeature {
+ module_name: "alpha_normalize",
+ directory: "alpha-normalization/success/",
+ variant: SpecTestKind::AlphaNormalization,
+ exclude_path: Rc::new(|path: &str| {
+ // This test is designed to not typecheck
+ path == "unit/FunctionNestedBindingXXFree"
+ }),
+ ..default_feature.clone()
+ },
+ TestFeature {
+ module_name: "type_inference_success",
+ directory: "type-inference/success/",
+ variant: SpecTestKind::TypeInferenceSuccess,
+ too_slow_path: Rc::new(|path: &str| path == "prelude"),
+ ..default_feature.clone()
+ },
+ TestFeature {
+ module_name: "type_inference_failure",
+ directory: "type-inference/failure/",
+ variant: SpecTestKind::TypeInferenceFailure,
+ exclude_path: Rc::new(|path: &str| {
+ false
+ // TODO: enable free variable checking
+ || path == "unit/MergeHandlerFreeVar"
+ // TODO: git changes newlines on windows
+ || cfg!(windows)
+ }),
+ output_type: FileType::UI,
+ ..default_feature
+ },
+ ]
+}
+
+fn run_test_stringy_error(test: &SpecTest) -> std::result::Result<(), String> {
+ let res = if env::var("CI_GRCOV").is_ok() {
+ let test: SpecTest = test.clone();
+ // Augment stack size when running with 0 inlining to avoid overflows
+ std::thread::Builder::new()
+ .stack_size(128 * 1024 * 1024)
+ .spawn(move || run_test(&test))
+ .unwrap()
+ .join()
+ .unwrap()
+ } else {
+ run_test(test)
+ };
+ res.map_err(|e| e.to_string())
+}
+
+fn run_test(test: &SpecTest) -> Result<()> {
+ use self::SpecTestKind::*;
+ // Setup current directory to the root of the repository. Important for `as Location` tests.
+ let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
+ .parent()
+ .unwrap()
+ .to_path_buf();
+ env::set_current_dir(root_dir.as_path())?;
+ // Set environment variable for import tests.
+ env::set_var("DHALL_TEST_VAR", "6 * 7");
+
+ let SpecTest {
+ input: expr,
+ output: expected,
+ ..
+ } = test;
+ match test.kind {
+ ParserSuccess => {
+ let expr = expr.parse()?;
+ // This exercices both parsing and binary decoding
+ expected.compare_debug(expr)?;
+ }
+ ParserFailure => {
+ use std::io;
+ let err = expr.parse().unwrap_err();
+ match err.downcast_ref::<DhallError>() {
+ Some(err) => match err.kind() {
+ ErrorKind::Parse(_) => {}
+ ErrorKind::IO(e)
+ if e.kind() == io::ErrorKind::InvalidData => {}
+ e => Err(TestError(format!(
+ "Expected parse error, got: {:?}",
+ e
+ )))?,
+ },
+ None => {}
+ }
+ expected.compare_ui(err)?;
+ }
+ BinaryEncoding => {
+ let expr = expr.parse()?;
+ expected.compare_binary(expr)?;
+ }
+ BinaryDecodingSuccess => {
+ let expr = expr.parse()?;
+ expected.compare_debug(expr)?;
+ }
+ BinaryDecodingFailure => {
+ let err = expr.parse().unwrap_err();
+ expected.compare_ui(err)?;
+ }
+ Printer => {
+ let parsed = expr.parse()?;
+ // Round-trip pretty-printer
+ let reparsed = Parsed::parse_str(&parsed.to_string())?;
+ assert_eq!(reparsed, parsed);
+ expected.compare_ui(parsed)?;
+ }
+ ImportSuccess => {
+ // Configure cache for import tests
+ env::set_var(
+ "XDG_CACHE_HOME",
+ root_dir
+ .join("dhall-lang")
+ .join("tests")
+ .join("import")
+ .join("cache")
+ .as_path(),
+ );
+ let expr = expr.normalize()?;
+ expected.compare(expr)?;
+ }
+ ImportFailure => {
+ let err = expr.resolve().unwrap_err();
+ expected.compare_ui(err)?;
+ }
+ SemanticHash => {
+ let expr = expr.normalize()?.to_expr_alpha();
+ let hash = hex::encode(expr.hash()?);
+ expected.compare_ui(format!("sha256:{}", hash))?;
+ }
+ TypeInferenceSuccess => {
+ let ty = expr.typecheck()?.get_type()?;
+ expected.compare(ty)?;
+ }
+ TypeInferenceFailure => {
+ let err = expr.typecheck().unwrap_err();
+ expected.compare_ui(err)?;
+ }
+ Normalization => {
+ let expr = expr.normalize()?;
+ expected.compare(expr)?;
+ }
+ AlphaNormalization => {
+ let expr = expr.normalize()?.to_expr_alpha();
+ expected.compare(expr)?;
+ }
+ }
+
+ Ok(())
+}
+
+fn main() {
+ let tests = define_features()
+ .into_iter()
+ .flat_map(discover_tests_for_feature)
+ .collect();
+
+ libtest_mimic::run_tests(&Arguments::from_args(), tests, |test| {
+ match run_test_stringy_error(&test.data) {
+ Ok(_) => Outcome::Passed,
+ Err(e) => Outcome::Failed { msg: Some(e) },
+ }
+ })
+ .exit();
+}