From 4bc7cd24a547baaab006bf134da74577c12f8a85 Mon Sep 17 00:00:00 2001 From: ilbertt Date: Fri, 20 Jun 2025 11:00:24 +0200 Subject: [PATCH 1/4] feat!: remove `didc test` subcommand --- rust/candid_parser/src/bindings/javascript.rs | 94 -------- rust/candid_parser/src/grammar.lalrpop | 25 --- rust/candid_parser/src/lib.rs | 1 - rust/candid_parser/src/test.rs | 200 ------------------ rust/candid_parser/tests/test_suite.rs | 24 --- tools/didc/README.md | 16 +- tools/didc/src/main.rs | 22 -- 7 files changed, 9 insertions(+), 373 deletions(-) delete mode 100644 rust/candid_parser/src/test.rs delete mode 100644 rust/candid_parser/tests/test_suite.rs diff --git a/rust/candid_parser/src/bindings/javascript.rs b/rust/candid_parser/src/bindings/javascript.rs index c0db60de6..823d0bb01 100644 --- a/rust/candid_parser/src/bindings/javascript.rs +++ b/rust/candid_parser/src/bindings/javascript.rs @@ -365,97 +365,3 @@ pub mod value { enclose("[", body, "]") } } - -pub mod test { - use super::value; - use crate::test::{HostAssert, HostTest, Test}; - use candid::pretty::utils::*; - use candid::TypeEnv; - use pretty::RcDoc; - - fn pp_hex(bytes: &[u8]) -> RcDoc { - str("Buffer.from('") - .append(RcDoc::as_string(hex::encode(bytes))) - .append("', 'hex')") - } - fn pp_encode<'a>(args: &'a candid::IDLArgs, tys: &'a [candid::types::Type]) -> RcDoc<'a> { - let vals = value::pp_args(args); - let tys = super::pp_rets(tys); - let items = [tys, vals]; - let params = concat(items.iter().cloned(), ","); - str("IDL.encode").append(enclose("(", params, ")")) - } - - fn pp_decode<'a>(bytes: &'a [u8], tys: &'a [candid::types::Type]) -> RcDoc<'a> { - let hex = pp_hex(bytes); - let tys = super::pp_rets(tys); - let items = [tys, hex]; - let params = concat(items.iter().cloned(), ","); - str("IDL.decode").append(enclose("(", params, ")")) - } - - pub fn test_generate(test: Test) -> String { - let header = r#"// AUTO-GENERATED. DO NOT EDIT. -// tslint:disable -import * as IDL from './idl'; -import { Buffer } from 'buffer/'; -import { Principal } from './principal'; -"#; - let mut res = header.to_string(); - let mut env = TypeEnv::new(); - crate::check_prog( - &mut env, - &crate::IDLProg { - decs: test.defs, - actor: None, - }, - ) - .unwrap(); - res += &super::compile(&env, &None); - for (i, assert) in test.asserts.iter().enumerate() { - let mut types = Vec::new(); - for ty in assert.typ.iter() { - types.push(crate::typing::ast_to_type(&env, ty).unwrap()); - } - let host = HostTest::from_assert(assert, &env, &types); - let mut expects = Vec::new(); - for cmd in host.asserts.iter() { - use HostAssert::*; - let test_func = match cmd { - Encode(args, tys, _, _) | NotEncode(args, tys) => { - let items = [super::pp_rets(tys), pp_encode(args, tys)]; - let params = concat(items.iter().cloned(), ","); - str("IDL.decode").append(enclose("(", params, ")")) - } - Decode(bytes, tys, _, _) | NotDecode(bytes, tys) => pp_decode(bytes, tys), - }; - let (test_func, predicate) = match cmd { - Encode(_, _, true, _) | Decode(_, _, true, _) => (test_func, str(".toEqual")), - Encode(_, _, false, _) | Decode(_, _, false, _) => { - (test_func, str(".not.toEqual")) - } - NotEncode(_, _) | NotDecode(_, _) => { - (str("() => ").append(test_func), str(".toThrow")) - } - }; - let expected = match cmd { - Encode(_, tys, _, bytes) => pp_decode(bytes, tys), - Decode(_, _, _, vals) => value::pp_args(vals), - NotEncode(_, _) | NotDecode(_, _) => RcDoc::nil(), - }; - let expect = enclose("expect(", test_func, ")") - .append(predicate) - .append(enclose("(", expected, ");")); - expects.push(expect); - } - let body = RcDoc::intersperse(expects.iter().cloned(), RcDoc::hardline()); - let body = str("test('") - .append(format!("{} {}", i + 1, host.desc)) - .append("', () => ") - .append(enclose_space("{", body, "});")) - .append(RcDoc::hardline()); - res += &body.pretty(LINE_WIDTH).to_string(); - } - res - } -} diff --git a/rust/candid_parser/src/grammar.lalrpop b/rust/candid_parser/src/grammar.lalrpop index 3a3f3bb05..0181f7e44 100644 --- a/rust/candid_parser/src/grammar.lalrpop +++ b/rust/candid_parser/src/grammar.lalrpop @@ -1,5 +1,4 @@ use super::types::{IDLType, PrimType, TypeField, FuncType, Binding, Dec, IDLProg, IDLTypes, IDLInitArgs, IDLArgType}; -use super::test::{Assert, Input, Test}; use super::token::{Token, error, error2, LexicalError, Span}; use candid::{Principal, types::Label}; use candid::types::value::{IDLField, IDLValue, IDLArgs, VariantValue}; @@ -282,30 +281,6 @@ pub IDLInitArgs: IDLInitArgs = { > => IDLInitArgs { decs, args } } -// Test file - -Input: Input = { - Text => Input::Text(<>), - Bytes => Input::Blob(<>), -} - -Assert: Assert = > =>? { - if id.0 != "assert" { - Err(error2("not an assert", id.1)) - } else { Ok(assert) } -}; - -Assertion: Assert = { - ":" => Assert { left, right: None, typ, pass: true, desc }, - "!:" => Assert { left, right: None, typ, pass: false, desc }, - "==" ":" => Assert { left, right: Some(right), typ, pass: true, desc }, - "!=" ":" => Assert { left, right: Some(right), typ, pass: false, desc }, -} - -pub Test: Test = { - > > => Test { defs, asserts }, -} - // Common util Name: String = { "id" => <>, diff --git a/rust/candid_parser/src/lib.rs b/rust/candid_parser/src/lib.rs index ca850bcdf..cf521b06f 100644 --- a/rust/candid_parser/src/lib.rs +++ b/rust/candid_parser/src/lib.rs @@ -140,7 +140,6 @@ pub mod configs; #[cfg_attr(docsrs, doc(cfg(feature = "random")))] #[cfg(feature = "random")] pub mod random; -pub mod test; pub fn parse_idl_args(s: &str) -> crate::Result { let lexer = token::Tokenizer::new(s); diff --git a/rust/candid_parser/src/test.rs b/rust/candid_parser/src/test.rs deleted file mode 100644 index 563b756a7..000000000 --- a/rust/candid_parser/src/test.rs +++ /dev/null @@ -1,200 +0,0 @@ -use super::types::{Dec, IDLProg, IDLType}; -use super::typing::check_prog; -use crate::{Error, Result}; -use candid::types::value::IDLArgs; -use candid::types::{Type, TypeEnv}; -use candid::DecoderConfig; - -const DECODING_COST: usize = 20_000_000; - -type TupType = Vec; - -pub struct Test { - pub defs: Vec, - pub asserts: Vec, -} - -pub struct Assert { - pub left: Input, - pub right: Option, - pub typ: TupType, - pub pass: bool, - pub desc: Option, -} - -pub enum Input { - Text(String), - Blob(Vec), -} - -/// Generate assertions for host value -pub struct HostTest { - pub desc: String, - pub asserts: Vec, -} -pub enum HostAssert { - // The encoded bytes is not unique - Encode(IDLArgs, Vec, bool, Vec), - NotEncode(IDLArgs, Vec), - Decode(Vec, Vec, bool, IDLArgs), - NotDecode(Vec, Vec), -} - -impl Assert { - pub fn desc(&self) -> String { - match &self.desc { - None => "", - Some(desc) => desc, - } - .to_string() - } -} - -impl Input { - pub fn parse(&self, env: &TypeEnv, types: &[Type]) -> Result { - match self { - Input::Text(ref s) => Ok(super::parse_idl_args(s)?.annotate_types(true, env, types)?), - Input::Blob(ref bytes) => { - let mut config = DecoderConfig::new(); - config.set_decoding_quota(DECODING_COST); - Ok(IDLArgs::from_bytes_with_types_with_config( - bytes, env, types, &config, - )?) - } - } - } - fn check_round_trip(&self, v: &IDLArgs, env: &TypeEnv, types: &[Type]) -> Result { - match self { - Input::Blob(ref blob) => { - let bytes = v.to_bytes_with_types(env, types)?; - Ok(*blob == bytes) - } - Input::Text(_) => Ok(true), //Ok(*s == v.to_string()), - } - } -} - -impl std::str::FromStr for Test { - type Err = Error; - fn from_str(str: &str) -> std::result::Result { - let lexer = super::token::Tokenizer::new(str); - Ok(super::grammar::TestParser::new().parse(lexer)?) - } -} - -impl HostTest { - pub fn from_assert(assert: &Assert, env: &TypeEnv, types: &[Type]) -> Self { - use HostAssert::*; - let types = types.to_vec(); - let mut asserts = Vec::new(); - match &assert.left { - Input::Text(s) => { - // Without type annotation, numbers are all of type int. - // Assertion may not pass. - let parsed = crate::parse_idl_args(s); - if parsed.is_err() { - let desc = format!("(skip) {}", assert.desc()); - return HostTest { desc, asserts }; - } - let parsed = parsed.unwrap(); - if !assert.pass && assert.right.is_none() { - asserts.push(NotEncode(parsed, types)); - } else { - let bytes = parsed.to_bytes_with_types(env, &types).unwrap(); - asserts.push(Encode(parsed.clone(), types.clone(), true, bytes.clone())); - // round tripping - let vals = parsed.annotate_types(true, env, &types).unwrap(); - asserts.push(Decode(bytes, types, true, vals)); - } - let desc = format!("(encode?) {}", assert.desc()); - HostTest { desc, asserts } - } - Input::Blob(bytes) => { - let bytes = bytes.to_vec(); - if !assert.pass && assert.right.is_none() { - asserts.push(NotDecode(bytes, types)); - } else { - let mut config = DecoderConfig::new(); - config.set_decoding_quota(DECODING_COST); - let args = - IDLArgs::from_bytes_with_types_with_config(&bytes, env, &types, &config) - .unwrap(); - asserts.push(Decode(bytes.clone(), types.clone(), true, args)); - // round tripping - // asserts.push(Encode(args, types.clone(), true, bytes.clone())); - if let Some(right) = &assert.right { - let expected = right.parse(env, &types).unwrap(); - if let Input::Blob(blob) = right { - asserts.push(Decode( - blob.to_vec(), - types.clone(), - true, - expected.clone(), - )); - } - if !assert.pass { - asserts.push(Decode(bytes, types, assert.pass, expected)); - } - } - } - HostTest { - desc: assert.desc(), - asserts, - } - } - } - } -} - -pub fn check(test: Test) -> Result<()> { - let mut env = TypeEnv::new(); - let prog = IDLProg { - decs: test.defs, - actor: None, - }; - check_prog(&mut env, &prog)?; - let mut count = 0; - for (i, assert) in test.asserts.iter().enumerate() { - print!("Checking {} {}...", i + 1, assert.desc()); - let mut types = Vec::new(); - for ty in assert.typ.iter() { - types.push(super::typing::ast_to_type(&env, ty)?); - } - let input = assert.left.parse(&env, &types); - let pass = if let Some(assert_right) = &assert.right { - let left = input?; - let right = assert_right.parse(&env, &types)?; - if !assert.left.check_round_trip(&left, &env, &types)? - || !assert_right.check_round_trip(&right, &env, &types)? - { - print!("[round-trip failed] "); - } - let is_equal = left == right; - if assert.pass != is_equal { - print!(" left:{left}, right:{right} "); - } - assert.pass == is_equal - } else { - let res = assert.pass == input.is_ok(); - if assert.pass && !assert.left.check_round_trip(&input?, &env, &types)? { - print!("[round-trip failed] "); - } - res - }; - if pass { - count += 1; - println!("[pass]"); - } else { - println!("[fail]"); - } - } - if count == test.asserts.len() { - Ok(()) - } else { - Err(Error::msg(format!( - "{}/{} passed", - count, - test.asserts.len() - ))) - } -} diff --git a/rust/candid_parser/tests/test_suite.rs b/rust/candid_parser/tests/test_suite.rs deleted file mode 100644 index 83a07133a..000000000 --- a/rust/candid_parser/tests/test_suite.rs +++ /dev/null @@ -1,24 +0,0 @@ -use candid_parser::test::{check, Test}; - -#[test_generator::test_resources("test/*.test.did")] -fn decode_test(resource: &str) { - let path = std::env::current_dir() - .unwrap() - .join("../../") - .join(resource); - let test = std::fs::read_to_string(path).unwrap(); - let ast = test.parse::().unwrap(); - check(ast).unwrap(); -} - -#[test_generator::test_resources("test/*.test.did")] -fn js_test(resource: &str) { - use candid_parser::bindings::javascript::test::test_generate; - let path = std::env::current_dir() - .unwrap() - .join("../../") - .join(resource); - let test = std::fs::read_to_string(path).unwrap(); - let ast = test.parse::().unwrap(); - println!("{}", test_generate(ast)); -} diff --git a/tools/didc/README.md b/tools/didc/README.md index 0140f272d..9c5d83e50 100644 --- a/tools/didc/README.md +++ b/tools/didc/README.md @@ -3,18 +3,20 @@ A multi-purpose tool for Candid. ``` -didc 0.1.0 +didc 0.4.0 USAGE: didc SUBCOMMANDS: - check Type check Candid file - bind Binding for different languages - test Generate test suites for different languages - encode Encode Candid value - decode Decode Candid binary data - random Generate random Candid values + check Type check Candid file + bind Generate binding for different languages + hash Compute the hash of a field name + encode Encode Candid value + decode Decode Candid binary data + assist Generate textual Candid values based on a terminal dialog + random Generate random Candid values + subtype Check for subtyping ``` ## TOML config diff --git a/tools/didc/src/main.rs b/tools/didc/src/main.rs index 26e0836ab..f44f90efe 100644 --- a/tools/didc/src/main.rs +++ b/tools/didc/src/main.rs @@ -41,14 +41,6 @@ enum Command { /// Specifies a subset of methods to generate bindings. Allowed format: "-m foo,bar", "-m foo bar", "-m foo -m bar" methods: Vec, }, - /// Generate test suites for different languages - Test { - /// Specifies .test.did file for test suites generation - input: PathBuf, - #[clap(short, long, value_parser = ["js", "did"], default_value = "js")] - /// Specifies target language - target: String, - }, /// Compute the hash of a field name Hash { input: String }, /// Encode Candid value @@ -262,20 +254,6 @@ fn main() -> Result<()> { }; println!("{content}"); } - Command::Test { input, target } => { - let test = std::fs::read_to_string(&input) - .map_err(|_| Error::msg(format!("could not read file {}", input.display())))?; - let ast = pretty_parse::(input.to_str().unwrap(), &test)?; - let content = match target.as_str() { - "js" => candid_parser::bindings::javascript::test::test_generate(ast), - "did" => { - candid_parser::test::check(ast)?; - "".to_string() - } - _ => unreachable!(), - }; - println!("{content}"); - } Command::Hash { input } => { println!("{}", candid_parser::idl_hash(&input)); } From 73a5786d2b1fb8e4e662eb30c185e0858b7e07a1 Mon Sep 17 00:00:00 2001 From: ilbertt Date: Fri, 20 Jun 2025 11:02:50 +0200 Subject: [PATCH 2/4] refactor: remove test files --- test/README.md | 81 --------------- test/construct.test.did | 212 ---------------------------------------- test/overshoot.test.did | 32 ------ test/prim.test.did | 200 ------------------------------------- test/reference.test.did | 69 ------------- test/spacebomb.test.did | 37 ------- test/subtypes.test.did | 167 ------------------------------- 7 files changed, 798 deletions(-) delete mode 100644 test/README.md delete mode 100644 test/construct.test.did delete mode 100644 test/overshoot.test.did delete mode 100644 test/prim.test.did delete mode 100644 test/reference.test.did delete mode 100644 test/spacebomb.test.did delete mode 100644 test/subtypes.test.did diff --git a/test/README.md b/test/README.md deleted file mode 100644 index 3d37f98a8..000000000 --- a/test/README.md +++ /dev/null @@ -1,81 +0,0 @@ -Candid test data -================ - -This directory contains Candid test data, to be used to check Candid -implementations for compliance. - -This test suite covers - - * parsing Candid service descriptions - * given a Candid type, decoding binary Candid data, asserting success or failure - * given a Candid type, parsing textual Candid data, asserting success or failure - * comparing such decoded/parsed values, using the Host language’s notion of equality. - -The test suite currently does not cover _encoding_, because the Candid binary -format is not canonical. Implementers are advised to extend this test suite -with randomzied round-tripping tests (host value → candid → host value) to -cover encoding as well. - -Format -====== - -The test suite contains these kind of files: - - * `.good.did`: - A Candid service description, with valid candid syntax. - Implementations should be able to parse them. - - * `.bad.did`: - A Candid service description, with invalid candid syntax. - Implementations should reject parsing them. - - * `.test.did`: - A set of Candid tests, written in a grammar that extends the Candid type and value grammar: - - The root production is - ``` - ::= ;* ;* - ``` - to allow any number of type definitions followed by encoding tests with grammar - ``` - ::= - | assert : - | assert !: - | assert == : - | assert != : - ::= | blob - ::= ? - ``` - where the four forms assert - * that the input can be decoded/parsed at that type - * that the input cannot be decoded/parsed at that type - * that the two inputs values decode/parse successfully, - and the results are equal. - * that the two inputs values decode/parse successfully, - and the results are not equal. - - The last two forms refer to the host language's equality, and are useful to - assert that due to subtyping, certain information is ignored. - - The `` describes input in textual notation (``) or binary - format (`blob `). - - A `` is an optional description of the test, potentially useful in - the test suite output. - -A Candid implementation conforms to this test suite if - - * its service parser (if present) accepts all `*.good.did` files and rejects - all `*.bad.did` files in this directory. - * all tests from all `*.test.did` files succeed. - -Candid implementations that do not support parsing service files can ignore the -`*.good.did` and `*.bad.did` files. - -Candid implementations that do not support parsing the textual prepresentation -can ignore those tests where all inputs are textual. They should treat a test -that compares textual and binary values as plain decoding assertions on the -binary value. - -For example, `assert blob "DIDL\00\01\7e\00" == (true) : (bool)` should be -treated as `assert blob "DIDL\00\01\7e\00" : (bool)` by such an implementation. diff --git a/test/construct.test.did b/test/construct.test.did deleted file mode 100644 index 2b7dac652..000000000 --- a/test/construct.test.did +++ /dev/null @@ -1,212 +0,0 @@ -/* -Encoding tests for construct types - -Corresponding to spec version version 0.1.6 -*/ - -// Type definitions -type Opt = opt Opt; -type Vec = vec Vec; -type EmptyRecord = record { EmptyRecord }; -type OptRec = opt record { OptRec }; - -type List = opt record { head : int; tail : List }; -type List1 = opt List2; -type List2 = record { head : int; tail : List1 }; -type EmptyVariant = variant { 0: EmptyVariant }; -type VariantList = variant { nil; cons: record { head : int; tail : VariantList } }; - -type A1 = record { foo : int32; bar : bool }; -type A2 = record { foo : int32; bar : bool; bat: VariantList; baz: A1; bbb: List; bib: variant { foo; bar }; bab: A1 }; - -// Type table -assert blob "DIDL\00\00" : () "empty table"; -assert blob "DIDL\01\6e\6f\00" : () "unused type"; -assert blob "DIDL\02\6e\6f\6e\6f\00" : () "repeated types"; -assert blob "DIDL\01\6e\00\00" : () "recursive type"; -assert blob "DIDL\01\6e\00" !: () "type too short"; -assert blob "DIDL\01\00\00" !: () "vacuous type"; -assert blob "DIDL\02\6e\01\00\00" !: () "vacuous type"; -assert blob "DIDL\01\6e\01\00" !: () "table entry out of range"; -assert blob "DIDL\00\01\00" !: () "arg entry out of range"; -assert blob "DIDL\00\03\7f\7f" !: () "arg too short"; -assert blob "DIDL\00\02\7f\7f\7f" !: () "arg too long"; -assert blob "DIDL\00\01\6e\7f\00" !: () "non-primitive in arg list"; -assert blob "DIDL\01\7f\00" !: () "primitive type in the table"; -assert blob "DIDL\01\68\00" !: () "principal in the table"; -assert blob "DIDL\02\6d\7f\00\00" !: () "table entry in the table"; -assert blob "DIDL\01\00\00" !: () "table entry in the table (self-reference)"; -assert blob "DIDL\01\6e\6f\6e\6f\00" !: () "table too long"; - -// option -assert blob "DIDL\01\6e\7c\01\00\00" == "(null)" : (opt int) "opt: null"; -assert blob "DIDL\01\6e\02\01\00\00" !: (opt int) "opt: type out of range"; -assert blob "DIDL\01\6e\7c\01\00\01\2a" == "(opt 42)" : (opt int) "opt: 42"; -assert blob "DIDL\01\6e\7c\01\00\02\2a" !: (opt int) "opt: out of range"; -assert blob "DIDL\01\6e\7c\01\00\ff" !: (opt int) "opt: out of range"; -assert blob "DIDL\01\6e\7c\01\00\01" !: (opt int) "opt: too short"; -assert blob "DIDL\01\6e\7c\01\00\00\2a" !: (opt int) "opt: too long"; -assert blob "DIDL\02\6e\01\6e\7c\01\00\01\01\2a" == "(opt opt 42)" : (opt opt int) "opt: nested"; -assert blob "DIDL\01\6e\00\01\00\01\01\00" == "(opt opt null)" : (Opt) "opt: recursion"; -assert blob "DIDL\01\6e\00\01\00\01\01\00" == "(opt opt null)" : (opt opt opt empty) "opt: non-recursive type"; -assert blob "DIDL\02\6e\01\6e\00\01\00\01\01\00" == "(opt opt null)" : (Opt) "opt: mutual recursion"; -assert blob "DIDL\00\00" == "(null)" : (Opt) "opt: extra arg"; - -// vector -assert blob "DIDL\01\6d\7c\01\00\00" == "(vec {})" : (vec int) "vec: empty"; -assert blob "DIDL\01\6d\7c\01\00\00" : (vec int8) "vec: non subtype empty"; -assert blob "DIDL\01\6d\7c\01\00\02\01\02" == "(vec { 1; 2 })" : (vec int) "vec"; -assert blob "DIDL\01\6d\7b\01\00\02\01\02" == "(blob \"\\01\\02\")" : (vec nat8) "vec: blob"; -assert blob "DIDL\01\6d\00\01\00\00" == "(vec {})" : (Vec) "vec: recursive vector"; -assert blob "DIDL\01\6d\00\01\00\02\00\00" == "(vec { vec {}; vec {} })" : (Vec) "vec: tree"; -assert blob "DIDL\01\6d\00\01\00\02\00\00" == "(vec { vec {}; vec {} })" : (vec vec opt empty) "vec: non-recursive tree"; -assert blob "DIDL\01\6d\7f\01\00\e8\07" : (vec null) "vec null"; -assert blob "DIDL\01\6d\7f\01\00\e8\07" : (vec opt nat) "vec null <: vec opt nat"; -assert "(vec { 1; -1 })" !: (vec nat) "vec: type mismatch"; -assert blob "DIDL\01\6d\7c\01\00" !: (vec int) "vec: too short"; -assert blob "DIDL\01\6d\7c\01\00\02\01" !: (vec int) "vec: too short"; -assert blob "DIDL\01\6d\7c\01\00\01\01\02" !: (vec int) "vec: too long"; -assert blob "DIDL\02\6d\01\6c\00\01\00\05" : (vec record {}) "vec of records"; - -// record -assert blob "DIDL\01\6c\00\01\00" == "(record {})" : (record {}) "record: empty"; -assert blob "DIDL\01\6c\00\03\00\00\00" == "(record {}, record {}, record {})" : (record {}, record {}, record {}) "record: multiple records"; -assert blob "DIDL\01\6c\01\01\7c\01\00\2a" == "(record { 1 = 42 })" : (record {1:int}) "record"; -assert blob "DIDL\01\6c\01\01\7c\01\00\2a" == "(record { 1 = opt 42 })" : (record {1:opt int}) "record: opt"; -assert blob "DIDL\01\6c\01\01\7c\01\00\2a" == "(record { 1 = null })" : (record {1:reserved}) "record: reserved"; -assert blob "DIDL\01\6c\01\01\7c\01\00\2a" == "(record {})" : (record {}) "record: ignore fields"; -assert "(record { whatever = 0 })" == "(record {})" : (record {}) "record: ignore fields (textual)"; -assert blob "DIDL\01\6c\01\01\7c\01\00\2a" !: (record {2:int}) "record: missing field"; -assert blob "DIDL\01\6c\01\01\7c\01\00\2a" == "(record { 2 = null })" : (record {2:opt int}) "record: missing opt field"; -assert blob "DIDL\01\6c\00\01\00" == "(record { 2 = null })" : (record {2:null}) "record: missing null field"; -assert blob "DIDL\01\6c\02\00\7c\01\7e\01\00\2a\01" == "(record {42; true})" : (record {int; bool}) "record: tuple"; -assert blob "DIDL\01\6c\02\00\7c\01\7e\01\00\2a\01" == "(record {1 = true})" : (record {1:bool}) "record: ignore fields"; -assert blob "DIDL\01\6c\02\00\7c\01\7e\01\00\2a\01" !: (record {bool; int}) "record: type mismatch"; -assert blob "DIDL\01\6c\02\00\7c\00\7e\01\00\2a\01" !: (record {int; bool}) "record: duplicate fields"; -assert blob "DIDL\01\6c\02\01\7c\00\7e\01\00\2a\01" !: (record {1:int; 0:bool}) "record: unsorted"; -assert blob "DIDL\01\6c\02\d3\e3\aa\02\7e\86\8e\b7\02\7c\01\00\01\2a" - == "(record { foo = 42; bar = true})" : (record {foo:int; bar:bool}) "record: named fields"; -assert blob "DIDL\01\6c\01\cd\84\b0\05\7f\01\00" == "(record { \"☃\" = null })" : (record { "☃":null }) "record: unicode field"; -assert blob "DIDL\01\6c\01\80\e4\97\d0\12\7c\01\00\2a" !: (record {}) "record: field hash larger than u32"; -assert blob "DIDL\04\6c\01\00\01\6c\01\00\02\6c\01\00\03\6c\00\01\00" - == "(record { record { record { record {} } } })" : (record{record{record{record{}}}}) "record: nested"; -assert blob "DIDL\01\6c\01\00\00\01\00" !: (EmptyRecord) "record: empty recursion"; -assert blob "DIDL\02\6d\01\6c\01\00\01\01\00\01" !: (vec EmptyRecord) "vec of empty records"; -assert blob "DIDL\01\6c\02\00\7c\01\7e\01\00\2a" !: (record {int}) "record: value too short"; -assert blob "DIDL\01\6c\02\00\7c\01\7e\01\00\2a\01\00" !: (record {int}) "record: value too long"; -assert blob "DIDL\01\6c\02\00\7c\01\01\00\2a\01" !: (record {}) "record: type too short"; -assert blob "DIDL\01\6c\02\00\7c\01\00\2a\01" !: (record {}) "record: type too short"; -assert blob "DIDL\01\6c\02\00\7c\01\7e\02\7e\01\00\2a\01\00" !: (record {}) "record: type too long"; -assert blob "DIDL\01\6c\02\00\01\01\7e\01\00\00\00" !: (record {}) "record: type out of range"; -assert blob "DIDL\01\6c\02\00\01\01\7e\01\7c\2a" !: (reserved) "record: type out of range"; - -// opt -assert blob "DIDL\00\01\7f" == "(null)" : (opt empty) "opt: parsing null : null"; -assert blob "DIDL\01\6e\6f\01\00\00" == "(null)" : (opt empty) "opt: parsing null : opt empty"; -assert blob "DIDL\01\6e\7e\01\00\00" == "(null)" : (opt bool) "opt: parsing null : opt bool "; -assert blob "DIDL\01\6e\7e\01\00\01\00" == "(opt false)" : (opt bool) "opt: parsing opt false : opt bool"; -assert blob "DIDL\01\6e\7e\01\00\01\01" == "(opt true)" : (opt bool) "opt: parsing opt true : opt bool"; -assert blob "DIDL\01\6e\7e\01\00\01\02" !: (opt bool) "opt: parsing invalid bool at opt bool"; -assert blob "DIDL\00\00" == "(null)" : (opt bool) "opt: extra value"; - -// nested opt -assert blob "DIDL\02\6e\01\6e\7e\01\00\00" == "(null)" : (opt opt bool) "opt: parsing null : opt opt bool"; -assert blob "DIDL\02\6e\01\6e\7e\01\00\01\00" == "(opt null)" : (opt opt bool) "opt: parsing opt null : opt opt bool"; -assert blob "DIDL\02\6e\01\6e\7e\01\00\01\01\00" == "(opt opt false)" : (opt opt bool) "opt: parsing opt opt false : opt opt bool"; - -// opt subtype to constituent -assert blob "DIDL\00\01\7e\00" == "(opt false)" : (opt bool) "opt: parsing false : opt bool"; -assert blob "DIDL\00\01\7e\01" == "(opt true)" : (opt bool) "opt: parsing true : opt bool"; -assert blob "DIDL\00\01\7e\02" !: (opt bool) "opt: parsing invalid bool at opt bool"; -assert blob "DIDL\01\6e\7f\01\00\00" == "(null)" : (opt opt null) "opt: parsing (null : opt null) at opt opt null gives null, not opt null"; -assert blob "DIDL\00\01\7e\01" == "(null)" : (opt opt bool) "opt: parsing true : opt opt bool gives null"; -assert blob "DIDL\00\01\7e\01" == "(null)" : (Opt) "opt: parsing (true : bool) at \"fix opt\" gives null"; -assert blob "DIDL\01\6c\01\00\00\01\00" !: (OptRec) "opt: parsing \"fix record\" at \"fix record opt\" fails"; - -// special opt subtyping -assert blob "DIDL\00\01\70" == "(null)" : (opt nat) "reserved <: opt nat"; -assert blob "DIDL\01\6e\7e\01\00\00" == "(null)" : (opt nat) "null : opt bool <: opt nat"; -assert blob "DIDL\01\6e\7e\01\00\01\01" == "(null)" : (opt nat) "opt true : opt bool <: opt nat"; -assert blob "DIDL\01\6e\7e\01\00\01\02" !: (opt nat) "opt bool <: opt nat with invalid boolean value"; -assert blob "DIDL\02\6e\01\6e\7e\01\00\01\01\01" == "(null)" : (opt nat) "opt opt true : opt opt bool <: opt nat"; -assert blob "DIDL\02\6e\01\6e\7e\01\00\01\01\01" == "(opt null)" : (opt opt nat) "opt opt true : opt opt bool <: opt opt nat"; -assert blob "DIDL\02\6e\01\6b\01\00\7e\01\00\01\00\01" == "(null)" : (opt variant { 0 : int }) "opt: recovered coercion error under variant"; - -// special record field rules -assert blob "DIDL\01\6c\00\01\00" == "(record { foo = null })" : (record { foo : opt bool }) "missing optional field"; -assert blob "DIDL\01\6c\00\01\00" == "(record { foo = \"☃\" })" : (record { foo : reserved }) "missing reserved field"; - -// variant -assert "(variant {})" !: (variant {}) "variant: no empty value"; -assert blob "DIDL\01\6b\00\01\00" !: (variant {}) "variant: no empty value"; -assert blob "DIDL\01\6b\01\00\7f\01\00\00" == "(variant {0})" : (variant {0}) "variant: numbered field"; -assert blob "DIDL\01\6b\01\00\7f\01\00\00\2a" !: (variant {0:int}) "variant: type mismatch"; -assert blob "DIDL\01\6b\02\00\7f\01\7c\01\00\01\2a" : (variant {0:int; 1:int}) "variant: type mismatch in unused tag"; -assert blob "DIDL\01\6b\01\00\7f\01\00\00" == "(variant {0})" : (variant {0;1}) "variant: ignore field"; -assert blob "DIDL\01\6b\02\00\7f\01\7f\01\00\00" : (variant {0}) "variant {0;1} <: variant {0}"; -assert blob "DIDL\01\6b\02\00\7f\01\7f\01\00\00" == "(opt variant {0})" : (opt variant {0}) "variant {0;1} <: opt variant {0}"; -assert blob "DIDL\01\6b\02\00\7f\01\7f\01\00\01" == "(variant {1})" : (variant {0;1;2}) "variant: change index"; -assert blob "DIDL\01\6b\01\00\7f\01\00\00" !: (variant {1}) "variant: missing field"; -assert blob "DIDL\01\6b\01\00\7f\01\00\01" !: (variant {0}) "variant: index out of range"; -assert blob "DIDL\01\6b\02\00\7f\00\7f\01\00\00" !: (variant {0}) "variant: duplicate fields"; -assert blob "DIDL\01\6b\03\00\7f\01\7f\01\7f\01\00\00" !: (variant {0}) "variant: duplicate fields"; -assert blob "DIDL\01\6b\02\01\7f\00\7f\01\00\00" !: (variant {0;1}) "variant: unsorted"; -assert blob "DIDL\01\6b\03\00\7f\01\7f\ff\ff\ff\ff\0f\7f\01\00\00" : (variant {0;1;4294967295}) "variant: ok with maxInt"; -assert blob "DIDL\01\6b\03\00\7f\ff\ff\ff\ff\0f\7f\01\7f\01\00\00" !: (variant {0;1;4294967295}) "variant: unsorted with maxInt"; -assert blob "DIDL\01\6b\02\b3\d3\c9\01\7f\e6\fd\d5\01\7f\01\00\00" == "(variant { Bar })" : (variant { Foo; Bar; }) "variant: enum"; -assert blob "DIDL\01\6b\01\cd\84\b0\05\7f\01\00\00" == "(variant { \"☃\" })" : (variant { "☃" }) "variant: unicode field"; -assert blob "DIDL\01\6b\02\bc\8a\01\71\c5\fe\d2\01\71\01\00\00\04\67\6f\6f\64" - == "(variant { Ok = \"good\" })" : (variant { Ok: text; Err: text }) "variant: result"; -assert blob "DIDL\01\6b\02\9c\c2\01\71\e5\8e\b4\02\6f\01\00\00\04\67\6f\6f\64" - == "(variant { ok = \"good\" })" : (variant { ok: text; err: empty }) "variant: with empty"; -assert blob "DIDL\02\6b\02\9c\c2\01\71\e5\8e\b4\02\01\6c\01\00\01\01\00\00\04\67\6f\6f\64" - == "(variant { ok = \"good\" })" : (variant { ok: text; err: EmptyRecord }) "variant: with EmptyRecord"; -assert blob "DIDL\03\6b\03\b3\d3\c9\01\01\bb\d3\c9\01\02\e6\fd\d5\01\7f\6c\02\00\7e\01\7c\6c\02\61\7c\62\7d\01\00\00\01\2a" - == "(variant { Bar = record { true; 42 } })" : (variant { Foo; Bar: record { bool; int }; Baz: record { a: int; b: nat } }) "variant"; -assert blob "DIDL\01\6b\01\00\00\01\00\00\00\00\00\00\00\00\00\00\00\00\00" !: (EmptyVariant) "variant: empty recursion"; -assert blob "DIDL\01\6b\01\80\e4\97\d0\12\7c\01\00\00\2a" !: (reserved) "variant: field hash larger than u32"; -assert blob "DIDL\01\6b\02\00\7f\01\7c\01\00\01\2a" : (variant {0; 1:int}) "variant"; -assert blob "DIDL\01\6b\02\00\7f\01\7c\01\00\01" !: (variant {0; 1:int}) "variant: value too short"; -assert blob "DIDL\01\6b\02\00\7f\01\7c\01\00\01\2a\00" !: (variant {0; 1:int}) "variant: value too long"; -assert blob "DIDL\01\6b\02\00\01\01\7c\01\00\01\2a" !: (variant {0:reserved; 1:int}) "variant: type out of range"; -assert blob "DIDL\01\6b\02\00\7f\01\00\00" !: (variant {0; 1}) "variant: type too short"; -assert blob "DIDL\01\6b\03\00\7f\01\7f\01\00\00" !: (variant {0; 1}) "variant: type too long"; - -// list -assert blob "DIDL\02\6e\01\6c\02\a0\d2\ac\a8\04\7c\90\ed\da\e7\04\00\01\00\00" == "(null)" : (List) "record: empty list"; -assert blob "DIDL\02\6e\01\6c\02\a0\d2\ac\a8\04\7c\90\ed\da\e7\04\00\01\00\01\01\01\02\01\03\01\04\00" - == "(opt record { head = 1; tail = opt record { head = 2; tail = opt record { head = 3; tail = opt record { head = 4; tail = null }}}})" : (List) "record: list"; - -assert blob "DIDL\02\6e\01\6c\02\a0\d2\ac\a8\04\7c\90\ed\da\e7\04\00\01\00\01\01\01\02\00" - == blob "DIDL\02\6c\02\a0\d2\ac\a8\04\7c\90\ed\da\e7\04\01\6e\00\01\01\01\01\01\02\00" : (List) "record: reorder type table"; - -assert blob "DIDL\02\6e\01\6c\02\a0\d2\ac\a8\04\7c\90\ed\da\e7\04\00\01\00\01\01\01\02\00" - == "(opt record { head = 1; tail = opt record { head = 2; tail = null } })" : (List1) "record: mutual recursive list"; - -assert blob "DIDL\02\6b\02\d1\a7\cf\02\7f\f1\f3\92\8e\04\01\6c\02\a0\d2\ac\a8\04\7c\90\ed\da\e7\04\00\01\00\00" - == "(variant {nil})" : (VariantList) "variant: empty list"; - -assert blob "DIDL\02\6b\02\d1\a7\cf\02\7f\f1\f3\92\8e\04\01\6c\02\a0\d2\ac\a8\04\7c\90\ed\da\e7\04\00\01\00\01\01\01\02\00" - == "(variant { cons = record { head = 1; tail = variant { cons = record { head = 2; tail = variant { nil } } } } })" : (VariantList) "variant: list"; - -assert blob "DIDL\02\6b\02\d1\a7\cf\02\7f\f1\f3\92\8e\04\01\6c\02\a0\d2\ac\a8\04\7c\90\ed\da\e7\04\00\01\00\00" - == "(variant {nil}, null, null, null, null)" : (VariantList, opt VariantList, null, reserved, opt int) "variant: extra args"; - -assert blob "DIDL\02\6b\02\d1\a7\cf\02\7f\f1\f3\92\8e\04\01\6c\02\a0\d2\ac\a8\04\7c\90\ed\da\e7\04\00\01\00\00" !: (VariantList, opt int, vec int) "non-null extra args"; - -// skip fields -assert blob "DIDL\07\6c\07\c3\e3\aa\02\01\d3\e3\aa\02\7e\d5\e3\aa\02\02\db\e3\aa\02\01\a2\e5\aa\02\04\bb\f1\aa\02\06\86\8e\b7\02\75\6c\02\d3\e3\aa\02\7e\86\8e\b7\02\75\6b\02\d1\a7\cf\02\7f\f1\f3\92\8e\04\03\6c\02\a0\d2\ac\a8\04\7c\90\ed\da\e7\04\02\6e\05\6c\02\a0\d2\ac\a8\04\7c\90\ed\da\e7\04\04\6b\02\d3\e3\aa\02\7f\86\8e\b7\02\7f\01\00\01\0b\00\00\00\01\00\00\0a\00\00\00\01\14\00\00\2a\00\00\00" == "(record { foo = 42; bar = true; bat = variant { nil }; baz = record { foo = 10; bar = false }; bbb = opt record { head = 20; tail = null }; bib = variant { bar }; bab = record { foo = 11; bar = true}})" : (A2); - -assert blob "DIDL\07\6c\07\c3\e3\aa\02\01\d3\e3\aa\02\7e\d5\e3\aa\02\02\db\e3\aa\02\01\a2\e5\aa\02\04\bb\f1\aa\02\06\86\8e\b7\02\75\6c\02\d3\e3\aa\02\7e\86\8e\b7\02\75\6b\02\d1\a7\cf\02\7f\f1\f3\92\8e\04\03\6c\02\a0\d2\ac\a8\04\7c\90\ed\da\e7\04\02\6e\05\6c\02\a0\d2\ac\a8\04\7c\90\ed\da\e7\04\04\6b\02\d3\e3\aa\02\7f\86\8e\b7\02\7f\01\00\01\0b\00\00\00\01\00\00\0a\00\00\00\01\14\00\00\2a\00\00\00" == "(record { foo = 42; bar = true })" : (A1) "skip fields"; - -assert blob "DIDL\07\6c\07\c3\e3\aa\02\01\d3\e3\aa\02\7e\d5\e3\aa\02\02\db\e3\aa\02\01\a2\e5\aa\02\04\bb\f1\aa\02\06\86\8e\b7\02\75\6c\02\d3\e3\aa\02\7e\86\8e\b7\02\75\6b\02\d1\a7\cf\02\7f\f1\f3\92\8e\04\03\6c\02\a0\d2\ac\a8\04\7c\90\ed\da\e7\04\02\6e\05\6c\02\a0\d2\ac\a8\04\7c\90\ed\da\e7\04\04\6b\02\d3\e3\aa\02\7f\86\8e\b7\02\7f\01\00\01\0b\00\00\00\01\00\00\0a\00\00\00\01\14\00\00\2a\00\00\00" == "(record { bar = true; baz = record { bar = false }; bbb = opt record { head = 20 }})" : (record { bar: bool; baz: record { bar: bool }; bbb: opt record { head : int} }) "skip fields"; - -assert blob "DIDL\07\6c\07\c3\e3\aa\02\01\d3\e3\aa\02\7e\d5\e3\aa\02\02\db\e3\aa\02\01\a2\e5\aa\02\04\bb\f1\aa\02\06\86\8e\b7\02\75\6c\02\d3\e3\aa\02\7e\86\8e\b7\02\75\6b\02\d1\a7\cf\02\7f\f1\f3\92\8e\04\03\6c\02\a0\d2\ac\a8\04\7c\90\ed\da\e7\04\02\6e\05\6c\02\a0\d2\ac\a8\04\7c\90\ed\da\e7\04\04\6b\02\d3\e3\aa\02\7f\86\8e\b7\02\7f\01\00\01\0b\00\00\00\01\00\00\0a\00\00\00\01\14\00\00\2a\00\00\00" == "(record { bat = variant { nil } })" : (record { bat: variant { nil; cons: record { head : int; tail : VariantList }; new_field: empty } }) "new variant field"; - -assert blob "DIDL\07\6c\07\c3\e3\aa\02\01\d3\e3\aa\02\7e\d5\e3\aa\02\02\db\e3\aa\02\01\a2\e5\aa\02\04\bb\f1\aa\02\06\86\8e\b7\02\75\6c\02\d3\e3\aa\02\7e\86\8e\b7\02\75\6b\02\d1\a7\cf\02\7f\f1\f3\92\8e\04\03\6c\02\a0\d2\ac\a8\04\7c\90\ed\da\e7\04\02\6e\05\6c\02\a0\d2\ac\a8\04\7c\90\ed\da\e7\04\04\6b\02\d3\e3\aa\02\7f\86\8e\b7\02\7f\01\00\01\0b\00\00\00\01\00\00\0a\00\00\00\01\14\00\00\2a\00\00\00" !: (record { foo: int; new_field: bool }) "new record field"; - -// Future types -// This uses 0x67 for the “future type”; bump (well, decrement) once that -// becomes a concrete future type -assert blob "DIDL\01\67\00\02\00\7e\00\00\01" == "(null, true)" : (opt empty,bool) "skipping minimal future type"; -assert blob "DIDL\01\67\03ABC\02\00\7e\05\00hello\01" == "(null,true)" : (opt empty,bool) "skipping future type with data"; diff --git a/test/overshoot.test.did b/test/overshoot.test.did deleted file mode 100644 index 46a29a48b..000000000 --- a/test/overshoot.test.did +++ /dev/null @@ -1,32 +0,0 @@ -/* -Corresponding to spec version version 0.1.6 - -The tests in this file involve large leb-encoded values that indicate sizes (of -tables, arrays, blobs, etc.). In all cases, a decode should be able to catch -that eary by noticing that the overall input is too short to potentially -contain that number of elements. - -The number is always one billion (leb "\80\94\eb\dc\03"). A test suite could run -these tests with a memory limit, of, say 100MB and should still never run into -out of memory conditions, if these eager plausability checks are performed. -*/ - - -assert blob "DIDL\80\94\eb\dc\03\00" !: () "type table length"; -assert blob "DIDL\00\80\94\eb\dc\03" !: () "argument sequence length"; -assert blob "DIDL\00\01\71\80\94\eb\dc\03Motoko" !: (text) "text length"; -assert blob "DIDL\00\01\68\01\80\94\eb\dc\03Motoko" !: (principal) "principal length"; - -assert blob "DIDL\01\6c\80\94\eb\dc\03\00\7f\00\7f" !: () "record field number"; -assert blob "DIDL\01\6b\80\94\eb\dc\03\00\7f\00\7f" !: () "variant field number"; - - -// the next one is an interesting one: it would not work with (vec null) or -// (vec reserved) or similar, as values of these types have size 0. -// Some implementations may detect the overshoot eagerly, others not. -assert blob "DIDL\01\6d\7e\01\00\80\94\eb\dc\03\00\00\00" !: (vec bool) "vector length"; - -assert blob "DIDL\01\6a\68\68\68\68\68\00\00\00\00\00\00\00\00\00\00\00\00\00\00\68\68\68\68\68\68\00\68\68\7a\68\68\68\68\68\68\68\68\68\68\68\68\68\68\68\79\79\79\79\79\79\79\79\79\79\79\79\79\79\79\7a\79\79\79\79\79\79\79\79\79\7b\79\79\79\79\79\7f\00\79\79\79\79\79\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\04\00\00\00\00\01\01\68\68\1d\00\00\00\00\00\00\00\68\1f\00\00\00\00\00\00\00\00\68\00\44\44\44\44\44\44\49\44\4c\00\f7\01\7c\80\80\80\80\80\80\80\80\ff\ff\ff\ff\80\80\80\80\80\80\80\80\ff\ff\ff\ff\80\80\80\80\80\80\80\80\80\80\80\80\49\44\4c\01\6c\01\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\01" !: () "func arg length"; - -assert blob "DIDL\01\67\80\94\eb\dc\03\00\00" !: () "future type length"; -assert blob "DIDL\01\67\00\01\00\80\94\eb\dc\03\00\00" !: () "future value length"; diff --git a/test/prim.test.did b/test/prim.test.did deleted file mode 100644 index bfd3a4b91..000000000 --- a/test/prim.test.did +++ /dev/null @@ -1,200 +0,0 @@ -/* -Encoding tests for primitive types - -Corresponding to spec version version 0.1.6 -*/ - -// fundamentally wrong -assert blob "" !: () "empty"; -assert blob "\00\00" !: () "no magic bytes"; -assert blob "DADL" !: () "wrong magic bytes"; -assert blob "DADL\00\00" !: () "wrong magic bytes"; -assert blob "DIDL\80\00\00" : () "overlong typ table length"; -assert blob "DIDL\00\80\00" : () "overlong arg length"; - -// nullary input -assert blob "DIDL\00\00" : (); -assert blob "DIDL\00\00\00" !: () "nullary: too long"; -assert blob "DIDL\00\01\7f" : () "Additional parameters are ignored"; -assert blob "DIDL\00\01\6e" !: () "Not a primitive type"; -assert blob "DIDL\00\01\5e" !: () "Out of range type"; - -// Missing arguments -assert blob "DIDL\00\00" !: (nat) "missing argument: nat fails"; -assert blob "DIDL\00\00" !: (empty) "missing argument: empty fails"; -assert blob "DIDL\00\00" == "(null)" : (null) "missing argument: null"; -assert blob "DIDL\00\00" == "(null)" : (opt empty) "missing argument: opt empty"; -assert blob "DIDL\00\00" == "(null)" : (opt null) "missing argument: opt null"; -assert blob "DIDL\00\00" == "(null)" : (opt nat) "missing argument: opt nat"; -assert blob "DIDL\00\00" == blob "DIDL\00\01\70" : (reserved) "missing argument: reserved"; - -// primitive types -assert blob "DIDL\00\01\7f" : (null); -assert blob "DIDL\00\01\7e" !: (null) "wrong type"; -assert blob "DIDL\00\01\7f\00" !: (null) "null: too long"; - -assert blob "DIDL\00\01\7e\00" == "(false)" : (bool) "bool: false"; -assert blob "DIDL\00\01\7e\01" == "(true)" : (bool) "bool: true"; -assert blob "DIDL\00\01\7e" !: (bool) "bool: missing"; -assert blob "DIDL\00\01\7e\02" !: (bool) "bool: out of range"; -assert blob "DIDL\00\01\7e\ff" !: (bool) "bool: out of range"; - -assert blob "DIDL\00\01\7d\00" == "(0)" : (nat) "nat: 0"; -assert blob "DIDL\00\01\7d\01" == "(1)" : (nat) "nat: 1"; -assert blob "DIDL\00\01\7d\7f" == "(127)" : (nat) "nat: 0x7f"; -assert blob "DIDL\00\01\7d\80\01" == "(128)" : (nat) "nat: leb (two bytes)"; -assert blob "DIDL\00\01\7d\ff\7f" == "(16383)" : (nat) "nat: leb (two bytes, all bits)"; -assert blob "DIDL\00\01\7d\80" !: (nat) "nat: leb too short"; -assert blob "DIDL\00\01\7d\80\00" == "(0)" : (nat) "nat: leb overlong"; -assert blob "DIDL\00\01\7d\ff\00" == "(127)" : (nat) "nat: leb overlong"; -assert blob "DIDL\00\01\7d\80\80\98\f4\e9\b5\ca\6a" == "(60000000000000000)" : (nat) "nat: big number"; - -assert blob "DIDL\00\01\7c\00" == "(0)" : (int) "int: 0"; -assert blob "DIDL\00\01\7c\01" == "(1)" : (int) "int: 1"; -assert blob "DIDL\00\01\7c\7f" == "(-1)" : (int) "int: -1"; -assert blob "DIDL\00\01\7c\40" == "(-64)" : (int) "int: -64"; -assert blob "DIDL\00\01\7c\80\01" == "(128)" : (int) "int: leb (two bytes)"; -assert blob "DIDL\00\01\7c\80" !: (int) "int: leb too short"; -assert blob "DIDL\00\01\7c\80\00" == "(0)" : (int) "int: leb overlong (0s)"; -assert blob "DIDL\00\01\7c\ff\7f" == "(-1)" : (int) "int: leb overlong (1s)"; -assert blob "DIDL\00\01\7c\ff\00" == "(127)" : (int) "int: leb not overlong when signed"; -assert blob "DIDL\00\01\7c\80\7f" == "(-128)" : (int) "int: leb not overlong when signed"; -assert blob "DIDL\00\01\7c\80\80\98\f4\e9\b5\ca\ea\00" == "(60000000000000000)" : (int) "int: big number"; -assert blob "DIDL\00\01\7c\80\80\e8\8b\96\ca\b5\95\7f" == "(-60000000000000000)" : (int) "int: negative big number"; - -assert blob "DIDL\00\01\7d\00" == "(0)" : (int) "nat <: int: 0"; -assert blob "DIDL\00\01\7d\01" == "(1)" : (int) "nat <: int: 1"; -assert blob "DIDL\00\01\7d\7f" == "(127)" : (int) "nat <: int: leb (two bytes)"; -assert blob "DIDL\00\01\7d\80\01" == "(128)" : (int) "nat <: int: leb (two bytes)"; -assert blob "DIDL\00\01\7d\ff\7f" == "(16383)" : (int) "nat <: int: leb (two bytes, all bits)"; - -assert blob "DIDL\00\01\7b\00" == "(0)" : (nat8) "nat8: 0"; -assert blob "DIDL\00\01\7b\01" == "(1)" : (nat8) "nat8: 1"; -assert blob "DIDL\00\01\7b\ff" == "(255)" : (nat8) "nat8: 255"; -assert blob "DIDL\00\01\7b" !: (nat8) "nat8: too short"; -assert blob "DIDL\00\01\7b\00\00" !: (nat8) "nat8: too long"; - -assert blob "DIDL\00\01\7a\00\00" == "(0)" : (nat16) "nat16: 0"; -assert blob "DIDL\00\01\7a\01\00" == "(1)" : (nat16) "nat16: 1"; -assert blob "DIDL\00\01\7a\ff\00" == "(255)" : (nat16) "nat16: 255"; -assert blob "DIDL\00\01\7a\00\01" == "(256)" : (nat16) "nat16: 256"; -assert blob "DIDL\00\01\7a\ff\ff" == "(65535)" : (nat16) "nat16: 65535"; -assert blob "DIDL\00\01\7a" !: (nat16) "nat16: too short"; -assert blob "DIDL\00\01\7a\00" !: (nat16) "nat16: too short"; -assert blob "DIDL\00\01\7a\00\00\00" !: (nat16) "nat16: too long"; - -assert blob "DIDL\00\01\79\00\00\00\00" == "(0)" : (nat32) "nat32: 0"; -assert blob "DIDL\00\01\79\01\00\00\00" == "(1)" : (nat32) "nat32: 1"; -assert blob "DIDL\00\01\79\ff\00\00\00" == "(255)" : (nat32) "nat32: 255"; -assert blob "DIDL\00\01\79\00\01\00\00" == "(256)" : (nat32) "nat32: 256"; -assert blob "DIDL\00\01\79\ff\ff\00\00" == "(65535)" : (nat32) "nat32: 65535"; -assert blob "DIDL\00\01\79\ff\ff\ff\ff" == "(4294967295)" : (nat32) "nat32: 4294967295"; -assert blob "DIDL\00\01\79" !: (nat32) "nat32: too short"; -assert blob "DIDL\00\01\79\00" !: (nat32) "nat32: too short"; -assert blob "DIDL\00\01\79\00\00" !: (nat32) "nat32: too short"; -assert blob "DIDL\00\01\79\00\00\00" !: (nat32) "nat32: too short"; -assert blob "DIDL\00\01\79\00\00\00\00\00" !: (nat32) "nat32: too long"; - -assert blob "DIDL\00\01\78\00\00\00\00\00\00\00\00" == "(0)" : (nat64) "nat64: 0"; -assert blob "DIDL\00\01\78\01\00\00\00\00\00\00\00" == "(1)" : (nat64) "nat64: 1"; -assert blob "DIDL\00\01\78\ff\00\00\00\00\00\00\00" == "(255)" : (nat64) "nat64: 255"; -assert blob "DIDL\00\01\78\00\01\00\00\00\00\00\00" == "(256)" : (nat64) "nat64: 256"; -assert blob "DIDL\00\01\78\ff\ff\00\00\00\00\00\00" == "(65535)" : (nat64) "nat64: 65535"; -assert blob "DIDL\00\01\78\ff\ff\ff\ff\00\00\00\00" == "(4294967295)" : (nat64) "nat64: 4294967295"; -assert blob "DIDL\00\01\78\ff\ff\ff\ff\ff\ff\ff\ff" == "(18446744073709551615)" : (nat64) "nat64: 18446744073709551615"; -assert blob "DIDL\00\01\78" !: (nat64) "nat64: too short"; -assert blob "DIDL\00\01\78\00" !: (nat64) "nat64: too short"; -assert blob "DIDL\00\01\78\00\00" !: (nat64) "nat64: too short"; -assert blob "DIDL\00\01\78\00\00\00" !: (nat64) "nat64: too short"; -assert blob "DIDL\00\01\78\00\00\00\00" !: (nat64) "nat64: too short"; -assert blob "DIDL\00\01\78\00\00\00\00\00" !: (nat64) "nat64: too short"; -assert blob "DIDL\00\01\78\00\00\00\00\00\00" !: (nat64) "nat64: too short"; -assert blob "DIDL\00\01\78\00\00\00\00\00\00\00" !: (nat64) "nat64: too short"; -assert blob "DIDL\00\01\78\00\00\00\00\00\00\00\00\00" !: (nat64) "nat64: too long"; - -assert blob "DIDL\00\01\77\00" == "(0)" : (int8) "int8: 0"; -assert blob "DIDL\00\01\77\01" == "(1)" : (int8) "int8: 1"; -assert blob "DIDL\00\01\77\ff" == "(-1)" : (int8) "int8: -1"; -assert blob "DIDL\00\01\77" !: (int8) "int8: too short"; -assert blob "DIDL\00\01\77\00\00" !: (int8) "int8: too long"; - -assert blob "DIDL\00\01\76\00\00" == "(0)" : (int16) "int16: 0"; -assert blob "DIDL\00\01\76\01\00" == "(1)" : (int16) "int16: 1"; -assert blob "DIDL\00\01\76\ff\00" == "(255)" : (int16) "int16: 255"; -assert blob "DIDL\00\01\76\00\01" == "(256)" : (int16) "int16: 256"; -assert blob "DIDL\00\01\76\ff\ff" == "(-1)" : (int16) "int16: -1"; -assert blob "DIDL\00\01\76" !: (int16) "int16: too short"; -assert blob "DIDL\00\01\76\00" !: (int16) "int16: too short"; -assert blob "DIDL\00\01\76\00\00\00" !: (int16) "int16: too long"; - -assert blob "DIDL\00\01\75\00\00\00\00" == "(0)" : (int32) "int32: 0"; -assert blob "DIDL\00\01\75\01\00\00\00" == "(1)" : (int32) "int32: 1"; -assert blob "DIDL\00\01\75\ff\00\00\00" == "(255)" : (int32) "int32: 255"; -assert blob "DIDL\00\01\75\00\01\00\00" == "(256)" : (int32) "int32: 256"; -assert blob "DIDL\00\01\75\ff\ff\00\00" == "(65535)" : (int32) "int32: 65535"; -assert blob "DIDL\00\01\75\ff\ff\ff\ff" == "(-1)" : (int32) "int32: -1"; -assert blob "DIDL\00\01\75" !: (int32) "int32: too short"; -assert blob "DIDL\00\01\75\00" !: (int32) "int32: too short"; -assert blob "DIDL\00\01\75\00\00" !: (int32) "int32: too short"; -assert blob "DIDL\00\01\75\00\00\00" !: (int32) "int32: too short"; -assert blob "DIDL\00\01\75\00\00\00\00\00" !: (int32) "int32: too long"; - -assert blob "DIDL\00\01\74\00\00\00\00\00\00\00\00" == "(0)" : (int64) "int64: 0"; -assert blob "DIDL\00\01\74\01\00\00\00\00\00\00\00" == "(1)" : (int64) "int64: 1"; -assert blob "DIDL\00\01\74\ff\00\00\00\00\00\00\00" == "(255)" : (int64) "int64: 255"; -assert blob "DIDL\00\01\74\00\01\00\00\00\00\00\00" == "(256)" : (int64) "int64: 256"; -assert blob "DIDL\00\01\74\ff\ff\00\00\00\00\00\00" == "(65535)" : (int64) "int64: 65535"; -assert blob "DIDL\00\01\74\ff\ff\ff\ff\00\00\00\00" == "(4294967295)" : (int64) "int64: 4294967295"; -assert blob "DIDL\00\01\74\ff\ff\ff\ff\ff\ff\ff\ff" == "(-1)" : (int64) "int64: -1"; -assert blob "DIDL\00\01\74" !: (int64) "int64: too short"; -assert blob "DIDL\00\01\74\00" !: (int64) "int64: too short"; -assert blob "DIDL\00\01\74\00\00" !: (int64) "int64: too short"; -assert blob "DIDL\00\01\74\00\00\00" !: (int64) "int64: too short"; -assert blob "DIDL\00\01\74\00\00\00\00" !: (int64) "int64: too short"; -assert blob "DIDL\00\01\74\00\00\00\00\00" !: (int64) "int64: too short"; -assert blob "DIDL\00\01\74\00\00\00\00\00\00" !: (int64) "int64: too short"; -assert blob "DIDL\00\01\74\00\00\00\00\00\00\00" !: (int64) "int64: too short"; -assert blob "DIDL\00\01\74\00\00\00\00\00\00\00\00\00" !: (int64) "int64: too long"; - -assert blob "DIDL\00\01\73\00\00\00\00" == "(0.)" : (float32) "float32: 0"; -assert blob "DIDL\00\01\73\00\00\40\40" == "(3.)" : (float32) "float32: 3"; -assert blob "DIDL\00\01\73\00\00\00\3f" == "(0.5)" : (float32) "float32: 0.5"; -assert blob "DIDL\00\01\73\00\00\00\bf" == "(-0.5)" : (float32) "float32: -0.5"; -assert blob "DIDL\00\01\73\00\00" !: (float32) "float32: too short"; -assert blob "DIDL\00\01\73\00\00\00\00\00" !: (float32) "float32: too long"; - -assert blob "DIDL\00\01\72\00\00\00\00\00\00\00\00" == "(0.)" : (float64) "float64: 0"; -assert blob "DIDL\00\01\72\00\00\00\00\00\00\08\40" == "(3.)" : (float64) "float64: 3"; -assert blob "DIDL\00\01\72\00\00\00\00\00\00\e0\3f" == "(0.5)" : (float64) "float64: 0.5"; -assert blob "DIDL\00\01\72\00\00\00\00\00\00\e0\bf" == "(-0.5)" : (float64) "float64: -0.5"; -assert blob "DIDL\00\01\72\01\00\00\00\00\00\f0\7f" : (float64) "float64: NaN"; -assert blob "DIDL\00\01\72\ff\ff\ff\ff\ff\ff\ef\7f" : (float64) "float64: max value"; -assert blob "DIDL\00\01\72\00\00\00\00" !: (float64) "float64: too short"; -assert blob "DIDL\00\01\72\00\00\00\00\00\00\00\00\00" !: (float64) "float64: too long"; - -assert blob "DIDL\00\01\71\00" == "(\"\")" : (text) "text: empty string"; -assert blob "DIDL\00\01\71\06Motoko" == "(\"Motoko\")" : (text) "text: Motoko"; -assert blob "DIDL\00\01\71\05Motoko" !: (text) "text: too long"; -assert blob "DIDL\00\01\71\07Motoko" !: (text) "text: too short"; -assert blob "DIDL\00\01\71\86\00Motoko" : (text) "text: overlong length leb"; -assert blob "DIDL\00\01\71\03\e2\98\83" == "(\"☃\")" : (text) "text: Unicode"; -assert "(\"\\u{2603}\")" == "(\"☃\")" : (text) "text: Unicode escape"; -assert "(\"\\u{26_03}\")" == "(\"☃\")" : (text) "text: Unicode escape with underscore"; -assert "(\"\\u{2603\")" !: (text) "text: Unicode escape (unclosed)"; -assert blob "DIDL\00\01\71\03\e2\28\a1" !: (text) "text: Invalid utf8"; -assert blob "DIDL\00\01\71\02\e2\98\83" !: (text) "text: Unicode overshoots"; -assert blob "DIDL\00\01\71\06\09\0A\0D\22\27\5C" == "(\"\\t\\n\\r\\\"\\\'\\\\\")" : (text) "text: Escape sequences"; - -assert blob "DIDL\00\01\70" == blob "DIDL\00\01\7f" : (reserved) "reserved from null"; -assert blob "DIDL\00\01\70" == blob "DIDL\00\01\7e\01" : (reserved) "reserved from bool"; -assert blob "DIDL\00\01\70" == blob "DIDL\00\01\7d\80\01" : (reserved) "reserved from nat"; -assert blob "DIDL\00\01\70" == blob "DIDL\00\01\71\06Motoko" : (reserved) "reserved from text"; -assert blob "DIDL\00\00" : (reserved) "reserved extra value"; -assert blob "DIDL\00\01\71\05Motoko" !: (reserved) "reserved from too short text"; -assert blob "DIDL\00\01\71\03\e2\28\a1" !: (reserved) "reserved from invalid utf8 text"; - -assert blob "DIDL\00\01\6f" !: (empty) "cannot decode empty type"; -assert blob "DIDL\01\6e\6f\01\00\00" == "(null)" : (opt empty) "okay to decode non-empty value"; - -assert blob "DIDL\00\0a\7f\7e\7d\7c\7f\70\7f\7b\7a\79\01\2a\2a\2a\2a\00\2a\00\00\00" - == "(null, true, 42, 42, null, null, null, 42, 42, 42)" : (null, bool, nat, int, null, reserved, null, nat8, nat16, nat32) "multiple arguments"; diff --git a/test/reference.test.did b/test/reference.test.did deleted file mode 100644 index 51ff72ea4..000000000 --- a/test/reference.test.did +++ /dev/null @@ -1,69 +0,0 @@ -/* -Encoding tests for reference types - -Corresponding to spec version version 0.1.6 -*/ - -// principal -assert blob "DIDL\00\01\68\01\00" == "(principal \"aaaaa-aa\")" : (principal) "principal: ic0"; -assert blob "DIDL\00\01\68\01\03\ca\ff\ee" == "(principal \"w7x7r-cok77-xa\")" : (principal) "principal"; -assert blob "DIDL\00\01\68\01\09\ef\cd\ab\00\00\00\00\00\01" - == "(principal \"2chl6-4hpzw-vqaaa-aaaaa-c\")" : (principal) "principal"; -assert blob "DIDL\00\01\68\01\02\ca\ff" != "(principal \"w7x7r-cok77-xa\")" : (principal) "principal"; -assert blob "DIDL\00\01\68\03\ca\ff\ee" !: (principal) "principal: no tag"; -assert blob "DIDL\00\01\68\01\03\ca\ff" !: (principal) "principal: too short"; -assert blob "DIDL\00\01\68\01\03\ca\ff\ee\ee" !: (principal) "principal: too long"; -assert blob "DIDL\01\68\01\00\01\03\ca\ff\ee" !: (principal) "principal: not construct"; - -// service -assert blob "DIDL\00\01\68\01\03\ca\ff\ee" !: (service {}) "service: not principal"; -assert blob "DIDL\00\01\69\01\03\ca\ff\ee" !: (service {}) "service: not primitive type"; -assert "(principal \"w7x7r-cok77-xa\")" !: (service {}) "service: not principal"; -assert blob "DIDL\01\69\00\01\00\01\03\ca\ff\ee" == "(service \"w7x7r-cok77-xa\")" : (service {}) "service"; -assert blob "DIDL\01\69\00\01\00\01\03\ca\ff\ee" == "(service \"w7x7r-cok77-xa\")" : (service {}) "service"; -assert blob "DIDL\02\6a\01\71\01\7d\00\69\01\03foo\00\01\01\01\03\ca\ff\ee" - == "(service \"w7x7r-cok77-xa\")" : (service { foo : (text) -> (nat) }) "service"; -assert blob "DIDL\02\6a\01\71\01\7d\00\69\02\03foo\00\04foo\32\00\01\01\01\03\ca\ff\ee" - == "(service \"w7x7r-cok77-xa\")" : (service { foo : (text) -> (nat); foo2 : (text) -> (nat) }) "service"; - -assert blob "DIDL\02\6a\01\71\01\7d\00\69\03\03foo\00\04foo\32\00\01\01\01\03\ca\ff\ee" !: (service { foo : (text) -> (nat); foo2 : (text) -> (nat) }) "service: too long"; -assert blob "DIDL\02\6a\01\71\01\7d\00\69\01\03foo\00\04foo\32\00\01\01\01\03\ca\ff\ee" !: (service { foo : (text) -> (nat); foo2 : (text) -> (nat) }) "service: too short"; -assert blob "DIDL\02\6a\01\71\01\7d\00\69\02\04foo\32\00\03foo\00\01\01\01\03\ca\ff\ee" !: (service { foo : (text) -> (nat); foo2 : (text) -> (nat) }) "service: unsorted"; -assert blob "DIDL\02\6a\01\71\01\7d\00\69\02\09foobarbaz\00\06foobar\00\01\01\01\03\ca\ff\ee" !: (service { foobar : (text) -> (nat); foobarbaz : (text) -> (nat) }) "service: unsorted (but sorted by hash)"; -assert blob "DIDL\02\6a\01\71\01\7d\00\69\01\03\e2\28\a1\00\01\01\01\03\ca\ff\ee" !: (reserved) "service: invalid unicode"; -assert blob "DIDL\02\6a\01\71\01\7d\00\69\02\03foo\00\03foo\00\01\01\01\03\ca\ff\ee" !: (service { foo : (text) -> (nat) }) "service: duplicate"; - -assert blob "DIDL\05i\02\03foo\01\04\f0\9f\90\82\02j\00\00\01\02j\01\03\01\04\01\01n|l\00\01\00\01\00" - == "(service \"aaaaa-aa\")" : (service {"🐂":(opt int)->(record{}) query; foo:()->() oneway}) "service: unicode"; -assert blob "DIDL\02\6a\01\71\01\7d\00\69\01\03foo\68\01\01\01\03\ca\ff\ee" !: (service { foo: () -> () }) "service { foo: principal }"; -assert blob "DIDL\02\6e\7e\69\01\03foo\00\01\01\01\03\ca\ff\ee" !: (service { foo: () -> () }) "service { foo: opt bool }"; -assert blob "DIDL\02\69\01\03foo\01\6e\7e\01\01\01\03\ca\ff\ee" !: (service { foo: () -> () }) "service { foo: opt bool }"; - -// function -assert blob "DIDL\01\6a\00\00\00\01\00\01\01\03\ca\ff\ee\01\61" - == "(func \"w7x7r-cok77-xa\".\"a\")" : (func () -> ()) "func: quote name"; -assert blob "DIDL\01\6a\00\00\00\01\00\01\01\03\ca\ff\ee\01\61" - == "(func \"w7x7r-cok77-xa\".a)" : (func () -> ()) "func: non-quote name"; -assert blob "DIDL\01\6a\00\00\00\01\00\01\00\03\ca\ff\ee\01\61" !: (func () -> ()) "func: wrong tag"; -assert blob "DIDL\02j\02|}\01\01\01\01i\00\01\00\01\01\00\04\f0\9f\90\82" - == "(func \"aaaaa-aa\".\"🐂\")" : (func (int,nat) -> (service {}) query) "func: unicode"; -assert blob "DIDL\01\6a\01\68\01\7d\00\01\00\01\01\03\ca\ff\ee\03foo" - == "(func \"w7x7r-cok77-xa\".foo)" : (func (principal) -> (nat)) "func"; -assert blob "DIDL\01\6a\01\71\01\7d\01\01\01\00\01\01\03\ca\ff\ee\03foo" - == "(func \"w7x7r-cok77-xa\".foo)" : (func (text) -> (nat) query) "func: query"; -assert blob "DIDL\01\6a\01\71\01\7d\01\03\01\00\01\01\03\ca\ff\ee\03foo" - == "(func \"w7x7r-cok77-xa\".foo)" : (func (text) -> (nat) composite_query) "func: composite query"; -assert blob "DIDL\01\6a\01\71\01\7d\01\80\01\01\00\01\01\03\ca\ff\ee\03foo" !: (func (text) -> (nat) query) "func: unknown annotation"; -assert blob "DIDL\00\01\6a\01\01\03\ca\ff\ee\01\61" !: (func () -> ()) "func: not primitive"; -assert blob "DIDL\00\01\6a\01\03\ca\ff\ee\01\61" !: (func () -> ()) "func: no tag"; -assert blob "DIDL\01\6a\01\69\01\7d\00\01\00\01\01\03\ca\ff\ee\03foo" !: (func (service {}) -> (nat)) "func: service not in type table"; -assert blob "DIDL\01\6a\01\71\01\7d\01\03\01\00\01\01\03\ca\ff\ee\03foo" !: (func (text) -> (nat)) "func: invalid annotation"; - -// subtype -assert blob "DIDL\01\69\00\01\00\01\03\ca\ff\ee" !: (service { foo : (text) -> (nat) }) "service {} !<: service { foo : (text) -> (nat) }"; -assert blob "DIDL\02\6a\01\71\01\7d\01\01\69\01\03foo\00\01\01\01\03\ca\ff\ee" !: (service { foo : (text) -> (nat) }) "service { foo : (text) -> (nat) query } !<: service { foo : (text) -> (nat) }"; -assert blob "DIDL\02\6a\01\71\01\7d\00\69\01\03foo\00\01\01\01\03\ca\ff\ee" !: (service { foo : (text) -> (nat) query }) "service { foo : (text) -> (nat) } !<: service { foo : (text) -> (nat) query }"; -assert blob "DIDL\02\6a\01\71\01\7d\00\69\01\03foo\00\01\01\01\03\ca\ff\ee" == "(service \"w7x7r-cok77-xa\")" : (service {}) "service { foo : (text) -> (nat) } decodes at service {}"; - -assert blob "DIDL\01\6a\00\00\00\01\00\01\01\03\ca\ff\ee\03foo" !: (func (text) -> (nat)) "func () -> () !<: func (text) -> (nat) }"; -assert blob "DIDL\01\6a\01\71\01\7d\00\01\00\01\01\03\ca\ff\ee\03foo" == "(func \"w7x7r-cok77-xa\".foo)" : (func (text, opt text) -> ()) "func (text) -> (nat) decodes at func (text, opt text) -> () }"; diff --git a/test/spacebomb.test.did b/test/spacebomb.test.did deleted file mode 100644 index 1c6110465..000000000 --- a/test/spacebomb.test.did +++ /dev/null @@ -1,37 +0,0 @@ - -// Space bomb tests - -// Messages in this test all take a lot of time, memory and stack space to decode. -// With infinite resources, these are all valid Candid messages. -// When using Candid in a resource limited environment, for example one consensus round in a blockchain, -// an implementation with self-metering should reject these messages relatively early -// without going through the whole deserialisation process. - -// \80\94\eb\dc\03 is 1000_000_000 -// \80\ad\e2\04 is 10_000_000 -// \ff\ff\3f is 1_048_575 -// \80\b5\18 is 400_000 - - -// Plain decoding (unused arguments) -assert blob "DIDL\01\6d\7f\01\00\80\94\eb\dc\03" !: () "vec null (extra argument)"; -assert blob "DIDL\01\6d\70\01\00\80\94\eb\dc\03" !: () "vec reserved (extra argument)"; -assert blob "DIDL\04\6c\03\00\7f\01\01\02\02\6c\01\00\70\6c\00\6d\00\01\03\80\94\eb\dc\03" !: () "zero-sized record (extra argument)"; -assert blob "DIDL\02\6d\01\6d\7f\01\00\05\ff\ff\3f\ff\ff\3f\ff\ff\3f\ff\ff\3f\ff\ff\3f" !: () "vec vec null (extra argument)"; -assert blob "DIDL\02\6d\01\6c\00\01\00\80\ad\e2\04" !: () "vec record {} (extra argument)"; -assert blob "DIDL\17\6c\02\01\7f\02\7f\6c\02\01\00\02\00\6c\02\00\01\01\01\6c\02\00\02\01\02\6c\02\00\03\01\03\6c\02\00\04\01\04\6c\02\00\05\01\05\6c\02\00\06\01\06\6c\02\00\07\01\07\6c\02\00\08\01\08\6c\02\00\09\01\09\6c\02\00\0a\01\0a\6c\02\00\0b\01\0b\6c\02\00\0c\01\0c\6c\02\00\0d\02\0d\6c\02\00\0e\01\0e\6c\02\00\0f\01\0f\6c\02\00\10\01\10\6c\02\00\11\01\11\6c\02\00\12\01\12\6c\02\00\13\01\13\6e\14\6d\15\01\16\02\01\01" !: () "vec opt record with 2^20 null (extra argument)"; - -// Decoding to actual type -assert blob "DIDL\01\6d\7f\01\00\80\94\eb\dc\03" !: (vec opt nat) "vec null (not ignored)"; -assert blob "DIDL\01\6d\70\01\00\80\94\eb\dc\03" !: (vec reserved) "vec reserved (not ignored)"; -assert blob "DIDL\04\6c\03\00\7f\01\01\02\02\6c\01\00\70\6c\00\6d\00\01\03\80\94\eb\dc\03" !: (vec record {null;record{reserved};record{}}) "zero-sized record (not ignored)"; -assert blob "DIDL\02\6d\01\6d\7f\01\00\05\ff\ff\3f\ff\ff\3f\ff\ff\3f\ff\ff\3f\ff\ff\3f" !: (vec vec null) "vec vec null (not ignored)"; -assert blob "DIDL\02\6d\01\6c\00\01\00\80\ad\e2\04" !: (vec record {}) "vec record {} (not ignored)"; - -// Decoding under opt -assert blob "DIDL\01\6d\7f\01\00\80\94\eb\dc\03" !: (opt nat) "vec null (subtyping)"; -assert blob "DIDL\01\6d\70\01\00\80\94\eb\dc\03" !: (opt nat) "vec reserved (subtyping)"; -assert blob "DIDL\04\6c\03\00\7f\01\01\02\02\6c\01\00\70\6c\00\6d\00\01\03\80\94\eb\dc\03" !: (opt nat) "zero-sized record (subtyping)"; -assert blob "DIDL\02\6d\01\6d\7f\01\00\05\ff\ff\3f\ff\ff\3f\ff\ff\3f\ff\ff\3f\ff\ff\3f" !: (vec opt nat) "vec vec null (subtyping)"; -assert blob "DIDL\02\6d\01\6c\00\01\00\80\ad\e2\04" !: (opt nat) "vec record {} (subtyping)"; -assert blob "DIDL\17\6c\02\01\7f\02\7f\6c\02\01\00\02\00\6c\02\00\01\01\01\6c\02\00\02\01\02\6c\02\00\03\01\03\6c\02\00\04\01\04\6c\02\00\05\01\05\6c\02\00\06\01\06\6c\02\00\07\01\07\6c\02\00\08\01\08\6c\02\00\09\01\09\6c\02\00\0a\01\0a\6c\02\00\0b\01\0b\6c\02\00\0c\01\0c\6c\02\00\0d\02\0d\6c\02\00\0e\01\0e\6c\02\00\0f\01\0f\6c\02\00\10\01\10\6c\02\00\11\01\11\6c\02\00\12\01\12\6c\02\00\13\01\13\6e\14\6d\15\01\16\05\01\01\01\01\01" !: (vec opt record {}) "vec opt record with 2^20 null (subtyping)"; diff --git a/test/subtypes.test.did b/test/subtypes.test.did deleted file mode 100644 index cd92605a8..000000000 --- a/test/subtypes.test.did +++ /dev/null @@ -1,167 +0,0 @@ -/* -Encoding tests for subtype tests in decoders - -Corresponding to spec version version 0.1.6 - -This test file contains tests for the subtype check that decoders are expected -to perform upon references. - -The shortest way to trigger a test for `t1 <: t2` is to pass `(func () -> (t1))` -and decode at type `(opt func () -> (t2))`, and check if the result is a -`reference` or `null`. - -The patterns idioms here are thus - -assert blob "DIDL\01\6a\00\01XX00\01\00\01\01\00\01m" - == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (t2)) "t1 <: t2"; -assert blob "DIDL\01\6a\00\01XX\00\01\00\01\01\00\01m" - == "(null)" : (opt func () -> (t2)) "t1 (t2)) "t1 <: t2"; -assert blob "DIDL\02\6a\00\01\01\00XX\01\00\01\01\00\01m" - == "(null)" : (opt func () -> (t2)) "t1 (null)) "null <: null"; -assert blob "DIDL\01\6a\00\01\7e\00\01\00\01\01\00\01m" - == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (bool)) "bool <: bool"; -assert blob "DIDL\01\6a\00\01\7d\00\01\00\01\01\00\01m" - == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (nat)) "nat <: nat"; -assert blob "DIDL\01\6a\00\01\7c\00\01\00\01\01\00\01m" - == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (int)) "int <: int"; - -// more basic cases -assert blob "DIDL\01\6a\00\01\7d\00\01\00\01\01\00\01m" - == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (int)) "nat <: int"; -assert blob "DIDL\01\6a\00\01\7f\00\01\00\01\01\00\01m" - == "(null)" : (opt func () -> (nat)) "int (nat8)) "nat (nat)) "nat8 (opt bool)) "nat <: opt bool"; -assert blob "DIDL\02\6a\00\01\01\00\6e\7e\01\00\01\01\00\01m" - == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (opt bool)) "opt bool <: opt bool"; -assert blob "DIDL\01\6a\00\01\7e\00\01\00\01\01\00\01m" - == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (opt bool)) "bool <: opt bool"; -assert blob "DIDL\02\6a\00\01\01\00\6e\01\01\00\01\01\00\01m" - == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (opt opt nat)) "µ opt <: opt opt nat"; - -// optional record fields -assert blob "DIDL\02\6a\00\01\01\00\6c\00\01\00\01\01\00\01m" - == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (record {})) "record {} <: record {}"; -assert blob "DIDL\02\6a\00\01\01\00\6c\00\01\00\01\01\00\01m" - == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (record { a : opt empty })) "record {} <: record { a : opt empty }"; -assert blob "DIDL\02\6a\00\01\01\00\6c\00\01\00\01\01\00\01m" - == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (record { a : opt null })) "record {} <: record { a : opt null }"; -assert blob "DIDL\02\6a\00\01\01\00\6c\00\01\00\01\01\00\01m" - == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (record { a : reserved })) "record {} <: record { a : reserved }"; -assert blob "DIDL\02\6a\00\01\01\00\6c\00\01\00\01\01\00\01m" - == "(null)" : (opt func () -> (record { a : empty })) "record {} (record { a : nat })) "record {} (record { a : null })) "record {} <: record { a : null }"; - -// optional func results -assert blob "DIDL\02\6a\00\01\01\00\6a\00\00\00\01\00\01\01\00\01m" - == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (func () -> ())) "func () -> () <: func () -> ()"; -assert blob "DIDL\02\6a\00\01\01\00\6a\00\00\00\01\00\01\01\00\01m" - == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (func () -> (opt empty))) "func () -> () <: func () -> (opt empty)"; -assert blob "DIDL\02\6a\00\01\01\00\6a\00\00\00\01\00\01\01\00\01m" - == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (func () -> (opt null))) "func () -> () <: func () -> (opt null)"; -assert blob "DIDL\02\6a\00\01\01\00\6a\00\00\00\01\00\01\01\00\01m" - == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (func () -> (reserved))) "func () -> () <: func () -> (reserved)"; -assert blob "DIDL\02\6a\00\01\01\00\6a\00\00\00\01\00\01\01\00\01m" - == "(null)" : (opt func () -> (func () -> (empty))) "func () -> () (empty)"; -assert blob "DIDL\02\6a\00\01\01\00\6a\00\00\00\01\00\01\01\00\01m" - == "(null)" : (opt func () -> (func () -> (nat))) "func () -> () (nat)"; -assert blob "DIDL\02\6a\00\01\01\00\6a\00\00\00\01\00\01\01\00\01m" - == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (func () -> (null))) "func () -> () <: func () -> (null)"; - -// optional func arguments -assert blob "DIDL\03\6a\00\01\01\00\6a\01\02\00\00\6e\6f\01\00\01\01\00\01m" - == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (func () -> ())) "func (opt empty) -> () <: func () -> ()"; -assert blob "DIDL\03\6a\00\01\01\00\6a\01\02\00\00\6e\7f\01\00\01\01\00\01m" - == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (func () -> ())) "func (opt null) -> () <: func () -> ()"; -assert blob "DIDL\02\6a\00\01\01\00\6a\01\70\00\00\01\00\01\01\00\01m" - == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (func () -> ())) "func (reserved) -> () <: func () -> ()"; -assert blob "DIDL\02\6a\00\01\01\00\6a\01\6f\00\00\01\00\01\01\00\01m" - == "(null)" : (opt func () -> (func () -> ())) "func (empty) -> () ()"; -assert blob "DIDL\02\6a\00\01\01\00\6a\01\7d\00\00\01\00\01\01\00\01m" - == "(null)" : (opt func () -> (func () -> ())) "func (nat) -> () ()"; -assert blob "DIDL\02\6a\00\01\01\00\6a\01\7f\00\00\01\00\01\01\00\01m" - == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (func () -> ())) "func (null) -> () <: func () -> ()"; - -// variants -assert blob "DIDL\02\6a\00\01\01\00\6b\00\01\00\01\01\00\01m" - == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (variant {})) "variant {} <: variant {}"; -assert blob "DIDL\02\6a\00\01\01\00\6b\00\01\00\01\01\00\01m" - == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (variant {0 : nat})) "variant {} <: variant {0 : nat}"; -assert blob "DIDL\02\6a\00\01\01\00\6b\01\00\7d\01\00\01\01\00\01m" - == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (variant {0 : nat})) "variant {0 : nat} <: variant {0 : nat}"; -assert blob "DIDL\02\6a\00\01\01\00\6b\01\00\7e\01\00\01\01\00\01m" - == "(null)" : (opt func () -> (variant {0 : nat})) "variant {0 : bool} (variant {1 : bool})) "variant {0 : bool} (EmptyRecord)) "(µ record) <: (µ record)"; -//assert blob "DIDL\02\6a\00\01\01\00\6c\01\00\01\01\00\01\01\00\01m" -// == "(null)" : (opt func () -> (empty)) "(µ record) (EmptyRecord)) "empty <: (µ record)"; -assert blob "DIDL\02\6a\00\01\01\00\6c\01\00\01\01\00\01\01\00\01m" - == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (record {EmptyRecord})) "(µ record) <: record {µ record}"; -assert blob "DIDL\03\6a\00\01\01\00\6c\01\00\02\6c\01\00\02\01\00\01\01\00\01m" - == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (EmptyRecord)) "record {µ record} <: (µ record)"; -assert blob "DIDL\02\6a\00\01\01\00\6c\01\00\01\01\00\01\01\00\01m" - == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (MuRecordOpt)) "(µ record) <: (μ (record opt))"; - -assert blob "DIDL\02\6a\00\01\01\00\6b\01\00\01\01\00\01\01\00\01m" - == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (EmptyVariant)) "(µ variant) <: (µ variant)"; -assert blob "DIDL\02\6a\00\01\01\00\6b\01\00\01\01\00\01\01\00\01m" - == "(null)" : (opt func () -> (empty)) "(µ variant) (EmptyVariant)) "empty <: (µ variant)"; -assert blob "DIDL\02\6a\00\01\01\00\6b\01\00\01\01\00\01\01\00\01m" - == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (variant {0 : EmptyVariant})) "(µ variant) <: variant {µ variant}"; -assert blob "DIDL\03\6a\00\01\01\00\6b\01\00\02\6b\01\00\02\01\00\01\01\00\01m" - == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (EmptyVariant)) "variant {µ variant} <: (µ variant)"; - -assert blob "DIDL\02\6a\00\01\01\00\6d\01\01\00\01\01\00\01m" - == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (Vec)) "(µ vec) <: (µ vec)"; -assert blob "DIDL\02\6a\00\01\01\00\6d\01\01\00\01\01\00\01m" - == "(null)" : (opt func () -> (empty)) "(µ vec) (Vec)) "empty <: (µ vec)"; -assert blob "DIDL\02\6a\00\01\01\00\6d\01\01\00\01\01\00\01m" - == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (vec Vec)) "(µ vec) <: vec {µ vec}"; -assert blob "DIDL\03\6a\00\01\01\00\6d\02\6d\02\01\00\01\01\00\01m" - == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (Vec)) "vec {µ vec} <: (µ vec)"; - -// future types -// This uses 0x67 for the “future type”; bump (well, decrement) once that -// becomes a concrete future type - -assert blob "DIDL\02\6a\00\01\01\00\67\00\01\00\01\01\00\01m" - == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (opt empty)) "(future type) <: (opt empty)"; -assert blob "DIDL\02\6a\00\01\01\00\67\00\01\00\01\01\00\01m" - == "(null)" : (opt func () -> (nat)) "(future type) Date: Fri, 20 Jun 2025 11:04:09 +0200 Subject: [PATCH 3/4] chore: update changelog --- Changelog.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Changelog.md b/Changelog.md index e901a0513..c52086976 100644 --- a/Changelog.md +++ b/Changelog.md @@ -20,6 +20,11 @@ * Keeps argument names for Rust functions. +### didc + +* Breaking changes: + + The `didc test` subcommand has been removed. + ## 2025-05-15 ### Candid 0.10.14 From 5db8e10c551f0efaca90f5487e51c7dc5171a37f Mon Sep 17 00:00:00 2001 From: ilbertt Date: Wed, 25 Jun 2025 09:42:59 +0200 Subject: [PATCH 4/4] test: restore the Candid test suite --- rust/candid_parser/src/grammar.lalrpop | 25 +++ rust/candid_parser/src/lib.rs | 1 + rust/candid_parser/src/test.rs | 200 +++++++++++++++++++++++ rust/candid_parser/tests/test_suite.rs | 12 ++ test/README.md | 81 ++++++++++ test/construct.test.did | 212 +++++++++++++++++++++++++ test/overshoot.test.did | 32 ++++ test/prim.test.did | 200 +++++++++++++++++++++++ test/reference.test.did | 69 ++++++++ test/spacebomb.test.did | 37 +++++ test/subtypes.test.did | 167 +++++++++++++++++++ 11 files changed, 1036 insertions(+) create mode 100644 rust/candid_parser/src/test.rs create mode 100644 rust/candid_parser/tests/test_suite.rs create mode 100644 test/README.md create mode 100644 test/construct.test.did create mode 100644 test/overshoot.test.did create mode 100644 test/prim.test.did create mode 100644 test/reference.test.did create mode 100644 test/spacebomb.test.did create mode 100644 test/subtypes.test.did diff --git a/rust/candid_parser/src/grammar.lalrpop b/rust/candid_parser/src/grammar.lalrpop index 0181f7e44..28ec9df65 100644 --- a/rust/candid_parser/src/grammar.lalrpop +++ b/rust/candid_parser/src/grammar.lalrpop @@ -1,4 +1,5 @@ use super::types::{IDLType, PrimType, TypeField, FuncType, Binding, Dec, IDLProg, IDLTypes, IDLInitArgs, IDLArgType}; +use super::test::{Assert, Input, Test}; use super::token::{Token, error, error2, LexicalError, Span}; use candid::{Principal, types::Label}; use candid::types::value::{IDLField, IDLValue, IDLArgs, VariantValue}; @@ -281,6 +282,30 @@ pub IDLInitArgs: IDLInitArgs = { > => IDLInitArgs { decs, args } } +// Test file. Follows the "specification" in test/README.md + +Input: Input = { + Text => Input::Text(<>), + Bytes => Input::Blob(<>), +} + +Assert: Assert = > =>? { + if id.0 != "assert" { + Err(error2("not an assert", id.1)) + } else { Ok(assert) } +}; + +Assertion: Assert = { + ":" => Assert { left, right: None, typ, pass: true, desc }, + "!:" => Assert { left, right: None, typ, pass: false, desc }, + "==" ":" => Assert { left, right: Some(right), typ, pass: true, desc }, + "!=" ":" => Assert { left, right: Some(right), typ, pass: false, desc }, +} + +pub Test: Test = { + > > => Test { defs, asserts }, +} + // Common util Name: String = { "id" => <>, diff --git a/rust/candid_parser/src/lib.rs b/rust/candid_parser/src/lib.rs index cf521b06f..ca850bcdf 100644 --- a/rust/candid_parser/src/lib.rs +++ b/rust/candid_parser/src/lib.rs @@ -140,6 +140,7 @@ pub mod configs; #[cfg_attr(docsrs, doc(cfg(feature = "random")))] #[cfg(feature = "random")] pub mod random; +pub mod test; pub fn parse_idl_args(s: &str) -> crate::Result { let lexer = token::Tokenizer::new(s); diff --git a/rust/candid_parser/src/test.rs b/rust/candid_parser/src/test.rs new file mode 100644 index 000000000..563b756a7 --- /dev/null +++ b/rust/candid_parser/src/test.rs @@ -0,0 +1,200 @@ +use super::types::{Dec, IDLProg, IDLType}; +use super::typing::check_prog; +use crate::{Error, Result}; +use candid::types::value::IDLArgs; +use candid::types::{Type, TypeEnv}; +use candid::DecoderConfig; + +const DECODING_COST: usize = 20_000_000; + +type TupType = Vec; + +pub struct Test { + pub defs: Vec, + pub asserts: Vec, +} + +pub struct Assert { + pub left: Input, + pub right: Option, + pub typ: TupType, + pub pass: bool, + pub desc: Option, +} + +pub enum Input { + Text(String), + Blob(Vec), +} + +/// Generate assertions for host value +pub struct HostTest { + pub desc: String, + pub asserts: Vec, +} +pub enum HostAssert { + // The encoded bytes is not unique + Encode(IDLArgs, Vec, bool, Vec), + NotEncode(IDLArgs, Vec), + Decode(Vec, Vec, bool, IDLArgs), + NotDecode(Vec, Vec), +} + +impl Assert { + pub fn desc(&self) -> String { + match &self.desc { + None => "", + Some(desc) => desc, + } + .to_string() + } +} + +impl Input { + pub fn parse(&self, env: &TypeEnv, types: &[Type]) -> Result { + match self { + Input::Text(ref s) => Ok(super::parse_idl_args(s)?.annotate_types(true, env, types)?), + Input::Blob(ref bytes) => { + let mut config = DecoderConfig::new(); + config.set_decoding_quota(DECODING_COST); + Ok(IDLArgs::from_bytes_with_types_with_config( + bytes, env, types, &config, + )?) + } + } + } + fn check_round_trip(&self, v: &IDLArgs, env: &TypeEnv, types: &[Type]) -> Result { + match self { + Input::Blob(ref blob) => { + let bytes = v.to_bytes_with_types(env, types)?; + Ok(*blob == bytes) + } + Input::Text(_) => Ok(true), //Ok(*s == v.to_string()), + } + } +} + +impl std::str::FromStr for Test { + type Err = Error; + fn from_str(str: &str) -> std::result::Result { + let lexer = super::token::Tokenizer::new(str); + Ok(super::grammar::TestParser::new().parse(lexer)?) + } +} + +impl HostTest { + pub fn from_assert(assert: &Assert, env: &TypeEnv, types: &[Type]) -> Self { + use HostAssert::*; + let types = types.to_vec(); + let mut asserts = Vec::new(); + match &assert.left { + Input::Text(s) => { + // Without type annotation, numbers are all of type int. + // Assertion may not pass. + let parsed = crate::parse_idl_args(s); + if parsed.is_err() { + let desc = format!("(skip) {}", assert.desc()); + return HostTest { desc, asserts }; + } + let parsed = parsed.unwrap(); + if !assert.pass && assert.right.is_none() { + asserts.push(NotEncode(parsed, types)); + } else { + let bytes = parsed.to_bytes_with_types(env, &types).unwrap(); + asserts.push(Encode(parsed.clone(), types.clone(), true, bytes.clone())); + // round tripping + let vals = parsed.annotate_types(true, env, &types).unwrap(); + asserts.push(Decode(bytes, types, true, vals)); + } + let desc = format!("(encode?) {}", assert.desc()); + HostTest { desc, asserts } + } + Input::Blob(bytes) => { + let bytes = bytes.to_vec(); + if !assert.pass && assert.right.is_none() { + asserts.push(NotDecode(bytes, types)); + } else { + let mut config = DecoderConfig::new(); + config.set_decoding_quota(DECODING_COST); + let args = + IDLArgs::from_bytes_with_types_with_config(&bytes, env, &types, &config) + .unwrap(); + asserts.push(Decode(bytes.clone(), types.clone(), true, args)); + // round tripping + // asserts.push(Encode(args, types.clone(), true, bytes.clone())); + if let Some(right) = &assert.right { + let expected = right.parse(env, &types).unwrap(); + if let Input::Blob(blob) = right { + asserts.push(Decode( + blob.to_vec(), + types.clone(), + true, + expected.clone(), + )); + } + if !assert.pass { + asserts.push(Decode(bytes, types, assert.pass, expected)); + } + } + } + HostTest { + desc: assert.desc(), + asserts, + } + } + } + } +} + +pub fn check(test: Test) -> Result<()> { + let mut env = TypeEnv::new(); + let prog = IDLProg { + decs: test.defs, + actor: None, + }; + check_prog(&mut env, &prog)?; + let mut count = 0; + for (i, assert) in test.asserts.iter().enumerate() { + print!("Checking {} {}...", i + 1, assert.desc()); + let mut types = Vec::new(); + for ty in assert.typ.iter() { + types.push(super::typing::ast_to_type(&env, ty)?); + } + let input = assert.left.parse(&env, &types); + let pass = if let Some(assert_right) = &assert.right { + let left = input?; + let right = assert_right.parse(&env, &types)?; + if !assert.left.check_round_trip(&left, &env, &types)? + || !assert_right.check_round_trip(&right, &env, &types)? + { + print!("[round-trip failed] "); + } + let is_equal = left == right; + if assert.pass != is_equal { + print!(" left:{left}, right:{right} "); + } + assert.pass == is_equal + } else { + let res = assert.pass == input.is_ok(); + if assert.pass && !assert.left.check_round_trip(&input?, &env, &types)? { + print!("[round-trip failed] "); + } + res + }; + if pass { + count += 1; + println!("[pass]"); + } else { + println!("[fail]"); + } + } + if count == test.asserts.len() { + Ok(()) + } else { + Err(Error::msg(format!( + "{}/{} passed", + count, + test.asserts.len() + ))) + } +} diff --git a/rust/candid_parser/tests/test_suite.rs b/rust/candid_parser/tests/test_suite.rs new file mode 100644 index 000000000..730514ed4 --- /dev/null +++ b/rust/candid_parser/tests/test_suite.rs @@ -0,0 +1,12 @@ +use candid_parser::test::{check, Test}; + +#[test_generator::test_resources("test/*.test.did")] +fn decode_test(resource: &str) { + let path = std::env::current_dir() + .unwrap() + .join("../../") + .join(resource); + let test = std::fs::read_to_string(path).unwrap(); + let ast = test.parse::().unwrap(); + check(ast).unwrap(); +} diff --git a/test/README.md b/test/README.md new file mode 100644 index 000000000..3d37f98a8 --- /dev/null +++ b/test/README.md @@ -0,0 +1,81 @@ +Candid test data +================ + +This directory contains Candid test data, to be used to check Candid +implementations for compliance. + +This test suite covers + + * parsing Candid service descriptions + * given a Candid type, decoding binary Candid data, asserting success or failure + * given a Candid type, parsing textual Candid data, asserting success or failure + * comparing such decoded/parsed values, using the Host language’s notion of equality. + +The test suite currently does not cover _encoding_, because the Candid binary +format is not canonical. Implementers are advised to extend this test suite +with randomzied round-tripping tests (host value → candid → host value) to +cover encoding as well. + +Format +====== + +The test suite contains these kind of files: + + * `.good.did`: + A Candid service description, with valid candid syntax. + Implementations should be able to parse them. + + * `.bad.did`: + A Candid service description, with invalid candid syntax. + Implementations should reject parsing them. + + * `.test.did`: + A set of Candid tests, written in a grammar that extends the Candid type and value grammar: + + The root production is + ``` + ::= ;* ;* + ``` + to allow any number of type definitions followed by encoding tests with grammar + ``` + ::= + | assert : + | assert !: + | assert == : + | assert != : + ::= | blob + ::= ? + ``` + where the four forms assert + * that the input can be decoded/parsed at that type + * that the input cannot be decoded/parsed at that type + * that the two inputs values decode/parse successfully, + and the results are equal. + * that the two inputs values decode/parse successfully, + and the results are not equal. + + The last two forms refer to the host language's equality, and are useful to + assert that due to subtyping, certain information is ignored. + + The `` describes input in textual notation (``) or binary + format (`blob `). + + A `` is an optional description of the test, potentially useful in + the test suite output. + +A Candid implementation conforms to this test suite if + + * its service parser (if present) accepts all `*.good.did` files and rejects + all `*.bad.did` files in this directory. + * all tests from all `*.test.did` files succeed. + +Candid implementations that do not support parsing service files can ignore the +`*.good.did` and `*.bad.did` files. + +Candid implementations that do not support parsing the textual prepresentation +can ignore those tests where all inputs are textual. They should treat a test +that compares textual and binary values as plain decoding assertions on the +binary value. + +For example, `assert blob "DIDL\00\01\7e\00" == (true) : (bool)` should be +treated as `assert blob "DIDL\00\01\7e\00" : (bool)` by such an implementation. diff --git a/test/construct.test.did b/test/construct.test.did new file mode 100644 index 000000000..2b7dac652 --- /dev/null +++ b/test/construct.test.did @@ -0,0 +1,212 @@ +/* +Encoding tests for construct types + +Corresponding to spec version version 0.1.6 +*/ + +// Type definitions +type Opt = opt Opt; +type Vec = vec Vec; +type EmptyRecord = record { EmptyRecord }; +type OptRec = opt record { OptRec }; + +type List = opt record { head : int; tail : List }; +type List1 = opt List2; +type List2 = record { head : int; tail : List1 }; +type EmptyVariant = variant { 0: EmptyVariant }; +type VariantList = variant { nil; cons: record { head : int; tail : VariantList } }; + +type A1 = record { foo : int32; bar : bool }; +type A2 = record { foo : int32; bar : bool; bat: VariantList; baz: A1; bbb: List; bib: variant { foo; bar }; bab: A1 }; + +// Type table +assert blob "DIDL\00\00" : () "empty table"; +assert blob "DIDL\01\6e\6f\00" : () "unused type"; +assert blob "DIDL\02\6e\6f\6e\6f\00" : () "repeated types"; +assert blob "DIDL\01\6e\00\00" : () "recursive type"; +assert blob "DIDL\01\6e\00" !: () "type too short"; +assert blob "DIDL\01\00\00" !: () "vacuous type"; +assert blob "DIDL\02\6e\01\00\00" !: () "vacuous type"; +assert blob "DIDL\01\6e\01\00" !: () "table entry out of range"; +assert blob "DIDL\00\01\00" !: () "arg entry out of range"; +assert blob "DIDL\00\03\7f\7f" !: () "arg too short"; +assert blob "DIDL\00\02\7f\7f\7f" !: () "arg too long"; +assert blob "DIDL\00\01\6e\7f\00" !: () "non-primitive in arg list"; +assert blob "DIDL\01\7f\00" !: () "primitive type in the table"; +assert blob "DIDL\01\68\00" !: () "principal in the table"; +assert blob "DIDL\02\6d\7f\00\00" !: () "table entry in the table"; +assert blob "DIDL\01\00\00" !: () "table entry in the table (self-reference)"; +assert blob "DIDL\01\6e\6f\6e\6f\00" !: () "table too long"; + +// option +assert blob "DIDL\01\6e\7c\01\00\00" == "(null)" : (opt int) "opt: null"; +assert blob "DIDL\01\6e\02\01\00\00" !: (opt int) "opt: type out of range"; +assert blob "DIDL\01\6e\7c\01\00\01\2a" == "(opt 42)" : (opt int) "opt: 42"; +assert blob "DIDL\01\6e\7c\01\00\02\2a" !: (opt int) "opt: out of range"; +assert blob "DIDL\01\6e\7c\01\00\ff" !: (opt int) "opt: out of range"; +assert blob "DIDL\01\6e\7c\01\00\01" !: (opt int) "opt: too short"; +assert blob "DIDL\01\6e\7c\01\00\00\2a" !: (opt int) "opt: too long"; +assert blob "DIDL\02\6e\01\6e\7c\01\00\01\01\2a" == "(opt opt 42)" : (opt opt int) "opt: nested"; +assert blob "DIDL\01\6e\00\01\00\01\01\00" == "(opt opt null)" : (Opt) "opt: recursion"; +assert blob "DIDL\01\6e\00\01\00\01\01\00" == "(opt opt null)" : (opt opt opt empty) "opt: non-recursive type"; +assert blob "DIDL\02\6e\01\6e\00\01\00\01\01\00" == "(opt opt null)" : (Opt) "opt: mutual recursion"; +assert blob "DIDL\00\00" == "(null)" : (Opt) "opt: extra arg"; + +// vector +assert blob "DIDL\01\6d\7c\01\00\00" == "(vec {})" : (vec int) "vec: empty"; +assert blob "DIDL\01\6d\7c\01\00\00" : (vec int8) "vec: non subtype empty"; +assert blob "DIDL\01\6d\7c\01\00\02\01\02" == "(vec { 1; 2 })" : (vec int) "vec"; +assert blob "DIDL\01\6d\7b\01\00\02\01\02" == "(blob \"\\01\\02\")" : (vec nat8) "vec: blob"; +assert blob "DIDL\01\6d\00\01\00\00" == "(vec {})" : (Vec) "vec: recursive vector"; +assert blob "DIDL\01\6d\00\01\00\02\00\00" == "(vec { vec {}; vec {} })" : (Vec) "vec: tree"; +assert blob "DIDL\01\6d\00\01\00\02\00\00" == "(vec { vec {}; vec {} })" : (vec vec opt empty) "vec: non-recursive tree"; +assert blob "DIDL\01\6d\7f\01\00\e8\07" : (vec null) "vec null"; +assert blob "DIDL\01\6d\7f\01\00\e8\07" : (vec opt nat) "vec null <: vec opt nat"; +assert "(vec { 1; -1 })" !: (vec nat) "vec: type mismatch"; +assert blob "DIDL\01\6d\7c\01\00" !: (vec int) "vec: too short"; +assert blob "DIDL\01\6d\7c\01\00\02\01" !: (vec int) "vec: too short"; +assert blob "DIDL\01\6d\7c\01\00\01\01\02" !: (vec int) "vec: too long"; +assert blob "DIDL\02\6d\01\6c\00\01\00\05" : (vec record {}) "vec of records"; + +// record +assert blob "DIDL\01\6c\00\01\00" == "(record {})" : (record {}) "record: empty"; +assert blob "DIDL\01\6c\00\03\00\00\00" == "(record {}, record {}, record {})" : (record {}, record {}, record {}) "record: multiple records"; +assert blob "DIDL\01\6c\01\01\7c\01\00\2a" == "(record { 1 = 42 })" : (record {1:int}) "record"; +assert blob "DIDL\01\6c\01\01\7c\01\00\2a" == "(record { 1 = opt 42 })" : (record {1:opt int}) "record: opt"; +assert blob "DIDL\01\6c\01\01\7c\01\00\2a" == "(record { 1 = null })" : (record {1:reserved}) "record: reserved"; +assert blob "DIDL\01\6c\01\01\7c\01\00\2a" == "(record {})" : (record {}) "record: ignore fields"; +assert "(record { whatever = 0 })" == "(record {})" : (record {}) "record: ignore fields (textual)"; +assert blob "DIDL\01\6c\01\01\7c\01\00\2a" !: (record {2:int}) "record: missing field"; +assert blob "DIDL\01\6c\01\01\7c\01\00\2a" == "(record { 2 = null })" : (record {2:opt int}) "record: missing opt field"; +assert blob "DIDL\01\6c\00\01\00" == "(record { 2 = null })" : (record {2:null}) "record: missing null field"; +assert blob "DIDL\01\6c\02\00\7c\01\7e\01\00\2a\01" == "(record {42; true})" : (record {int; bool}) "record: tuple"; +assert blob "DIDL\01\6c\02\00\7c\01\7e\01\00\2a\01" == "(record {1 = true})" : (record {1:bool}) "record: ignore fields"; +assert blob "DIDL\01\6c\02\00\7c\01\7e\01\00\2a\01" !: (record {bool; int}) "record: type mismatch"; +assert blob "DIDL\01\6c\02\00\7c\00\7e\01\00\2a\01" !: (record {int; bool}) "record: duplicate fields"; +assert blob "DIDL\01\6c\02\01\7c\00\7e\01\00\2a\01" !: (record {1:int; 0:bool}) "record: unsorted"; +assert blob "DIDL\01\6c\02\d3\e3\aa\02\7e\86\8e\b7\02\7c\01\00\01\2a" + == "(record { foo = 42; bar = true})" : (record {foo:int; bar:bool}) "record: named fields"; +assert blob "DIDL\01\6c\01\cd\84\b0\05\7f\01\00" == "(record { \"☃\" = null })" : (record { "☃":null }) "record: unicode field"; +assert blob "DIDL\01\6c\01\80\e4\97\d0\12\7c\01\00\2a" !: (record {}) "record: field hash larger than u32"; +assert blob "DIDL\04\6c\01\00\01\6c\01\00\02\6c\01\00\03\6c\00\01\00" + == "(record { record { record { record {} } } })" : (record{record{record{record{}}}}) "record: nested"; +assert blob "DIDL\01\6c\01\00\00\01\00" !: (EmptyRecord) "record: empty recursion"; +assert blob "DIDL\02\6d\01\6c\01\00\01\01\00\01" !: (vec EmptyRecord) "vec of empty records"; +assert blob "DIDL\01\6c\02\00\7c\01\7e\01\00\2a" !: (record {int}) "record: value too short"; +assert blob "DIDL\01\6c\02\00\7c\01\7e\01\00\2a\01\00" !: (record {int}) "record: value too long"; +assert blob "DIDL\01\6c\02\00\7c\01\01\00\2a\01" !: (record {}) "record: type too short"; +assert blob "DIDL\01\6c\02\00\7c\01\00\2a\01" !: (record {}) "record: type too short"; +assert blob "DIDL\01\6c\02\00\7c\01\7e\02\7e\01\00\2a\01\00" !: (record {}) "record: type too long"; +assert blob "DIDL\01\6c\02\00\01\01\7e\01\00\00\00" !: (record {}) "record: type out of range"; +assert blob "DIDL\01\6c\02\00\01\01\7e\01\7c\2a" !: (reserved) "record: type out of range"; + +// opt +assert blob "DIDL\00\01\7f" == "(null)" : (opt empty) "opt: parsing null : null"; +assert blob "DIDL\01\6e\6f\01\00\00" == "(null)" : (opt empty) "opt: parsing null : opt empty"; +assert blob "DIDL\01\6e\7e\01\00\00" == "(null)" : (opt bool) "opt: parsing null : opt bool "; +assert blob "DIDL\01\6e\7e\01\00\01\00" == "(opt false)" : (opt bool) "opt: parsing opt false : opt bool"; +assert blob "DIDL\01\6e\7e\01\00\01\01" == "(opt true)" : (opt bool) "opt: parsing opt true : opt bool"; +assert blob "DIDL\01\6e\7e\01\00\01\02" !: (opt bool) "opt: parsing invalid bool at opt bool"; +assert blob "DIDL\00\00" == "(null)" : (opt bool) "opt: extra value"; + +// nested opt +assert blob "DIDL\02\6e\01\6e\7e\01\00\00" == "(null)" : (opt opt bool) "opt: parsing null : opt opt bool"; +assert blob "DIDL\02\6e\01\6e\7e\01\00\01\00" == "(opt null)" : (opt opt bool) "opt: parsing opt null : opt opt bool"; +assert blob "DIDL\02\6e\01\6e\7e\01\00\01\01\00" == "(opt opt false)" : (opt opt bool) "opt: parsing opt opt false : opt opt bool"; + +// opt subtype to constituent +assert blob "DIDL\00\01\7e\00" == "(opt false)" : (opt bool) "opt: parsing false : opt bool"; +assert blob "DIDL\00\01\7e\01" == "(opt true)" : (opt bool) "opt: parsing true : opt bool"; +assert blob "DIDL\00\01\7e\02" !: (opt bool) "opt: parsing invalid bool at opt bool"; +assert blob "DIDL\01\6e\7f\01\00\00" == "(null)" : (opt opt null) "opt: parsing (null : opt null) at opt opt null gives null, not opt null"; +assert blob "DIDL\00\01\7e\01" == "(null)" : (opt opt bool) "opt: parsing true : opt opt bool gives null"; +assert blob "DIDL\00\01\7e\01" == "(null)" : (Opt) "opt: parsing (true : bool) at \"fix opt\" gives null"; +assert blob "DIDL\01\6c\01\00\00\01\00" !: (OptRec) "opt: parsing \"fix record\" at \"fix record opt\" fails"; + +// special opt subtyping +assert blob "DIDL\00\01\70" == "(null)" : (opt nat) "reserved <: opt nat"; +assert blob "DIDL\01\6e\7e\01\00\00" == "(null)" : (opt nat) "null : opt bool <: opt nat"; +assert blob "DIDL\01\6e\7e\01\00\01\01" == "(null)" : (opt nat) "opt true : opt bool <: opt nat"; +assert blob "DIDL\01\6e\7e\01\00\01\02" !: (opt nat) "opt bool <: opt nat with invalid boolean value"; +assert blob "DIDL\02\6e\01\6e\7e\01\00\01\01\01" == "(null)" : (opt nat) "opt opt true : opt opt bool <: opt nat"; +assert blob "DIDL\02\6e\01\6e\7e\01\00\01\01\01" == "(opt null)" : (opt opt nat) "opt opt true : opt opt bool <: opt opt nat"; +assert blob "DIDL\02\6e\01\6b\01\00\7e\01\00\01\00\01" == "(null)" : (opt variant { 0 : int }) "opt: recovered coercion error under variant"; + +// special record field rules +assert blob "DIDL\01\6c\00\01\00" == "(record { foo = null })" : (record { foo : opt bool }) "missing optional field"; +assert blob "DIDL\01\6c\00\01\00" == "(record { foo = \"☃\" })" : (record { foo : reserved }) "missing reserved field"; + +// variant +assert "(variant {})" !: (variant {}) "variant: no empty value"; +assert blob "DIDL\01\6b\00\01\00" !: (variant {}) "variant: no empty value"; +assert blob "DIDL\01\6b\01\00\7f\01\00\00" == "(variant {0})" : (variant {0}) "variant: numbered field"; +assert blob "DIDL\01\6b\01\00\7f\01\00\00\2a" !: (variant {0:int}) "variant: type mismatch"; +assert blob "DIDL\01\6b\02\00\7f\01\7c\01\00\01\2a" : (variant {0:int; 1:int}) "variant: type mismatch in unused tag"; +assert blob "DIDL\01\6b\01\00\7f\01\00\00" == "(variant {0})" : (variant {0;1}) "variant: ignore field"; +assert blob "DIDL\01\6b\02\00\7f\01\7f\01\00\00" : (variant {0}) "variant {0;1} <: variant {0}"; +assert blob "DIDL\01\6b\02\00\7f\01\7f\01\00\00" == "(opt variant {0})" : (opt variant {0}) "variant {0;1} <: opt variant {0}"; +assert blob "DIDL\01\6b\02\00\7f\01\7f\01\00\01" == "(variant {1})" : (variant {0;1;2}) "variant: change index"; +assert blob "DIDL\01\6b\01\00\7f\01\00\00" !: (variant {1}) "variant: missing field"; +assert blob "DIDL\01\6b\01\00\7f\01\00\01" !: (variant {0}) "variant: index out of range"; +assert blob "DIDL\01\6b\02\00\7f\00\7f\01\00\00" !: (variant {0}) "variant: duplicate fields"; +assert blob "DIDL\01\6b\03\00\7f\01\7f\01\7f\01\00\00" !: (variant {0}) "variant: duplicate fields"; +assert blob "DIDL\01\6b\02\01\7f\00\7f\01\00\00" !: (variant {0;1}) "variant: unsorted"; +assert blob "DIDL\01\6b\03\00\7f\01\7f\ff\ff\ff\ff\0f\7f\01\00\00" : (variant {0;1;4294967295}) "variant: ok with maxInt"; +assert blob "DIDL\01\6b\03\00\7f\ff\ff\ff\ff\0f\7f\01\7f\01\00\00" !: (variant {0;1;4294967295}) "variant: unsorted with maxInt"; +assert blob "DIDL\01\6b\02\b3\d3\c9\01\7f\e6\fd\d5\01\7f\01\00\00" == "(variant { Bar })" : (variant { Foo; Bar; }) "variant: enum"; +assert blob "DIDL\01\6b\01\cd\84\b0\05\7f\01\00\00" == "(variant { \"☃\" })" : (variant { "☃" }) "variant: unicode field"; +assert blob "DIDL\01\6b\02\bc\8a\01\71\c5\fe\d2\01\71\01\00\00\04\67\6f\6f\64" + == "(variant { Ok = \"good\" })" : (variant { Ok: text; Err: text }) "variant: result"; +assert blob "DIDL\01\6b\02\9c\c2\01\71\e5\8e\b4\02\6f\01\00\00\04\67\6f\6f\64" + == "(variant { ok = \"good\" })" : (variant { ok: text; err: empty }) "variant: with empty"; +assert blob "DIDL\02\6b\02\9c\c2\01\71\e5\8e\b4\02\01\6c\01\00\01\01\00\00\04\67\6f\6f\64" + == "(variant { ok = \"good\" })" : (variant { ok: text; err: EmptyRecord }) "variant: with EmptyRecord"; +assert blob "DIDL\03\6b\03\b3\d3\c9\01\01\bb\d3\c9\01\02\e6\fd\d5\01\7f\6c\02\00\7e\01\7c\6c\02\61\7c\62\7d\01\00\00\01\2a" + == "(variant { Bar = record { true; 42 } })" : (variant { Foo; Bar: record { bool; int }; Baz: record { a: int; b: nat } }) "variant"; +assert blob "DIDL\01\6b\01\00\00\01\00\00\00\00\00\00\00\00\00\00\00\00\00" !: (EmptyVariant) "variant: empty recursion"; +assert blob "DIDL\01\6b\01\80\e4\97\d0\12\7c\01\00\00\2a" !: (reserved) "variant: field hash larger than u32"; +assert blob "DIDL\01\6b\02\00\7f\01\7c\01\00\01\2a" : (variant {0; 1:int}) "variant"; +assert blob "DIDL\01\6b\02\00\7f\01\7c\01\00\01" !: (variant {0; 1:int}) "variant: value too short"; +assert blob "DIDL\01\6b\02\00\7f\01\7c\01\00\01\2a\00" !: (variant {0; 1:int}) "variant: value too long"; +assert blob "DIDL\01\6b\02\00\01\01\7c\01\00\01\2a" !: (variant {0:reserved; 1:int}) "variant: type out of range"; +assert blob "DIDL\01\6b\02\00\7f\01\00\00" !: (variant {0; 1}) "variant: type too short"; +assert blob "DIDL\01\6b\03\00\7f\01\7f\01\00\00" !: (variant {0; 1}) "variant: type too long"; + +// list +assert blob "DIDL\02\6e\01\6c\02\a0\d2\ac\a8\04\7c\90\ed\da\e7\04\00\01\00\00" == "(null)" : (List) "record: empty list"; +assert blob "DIDL\02\6e\01\6c\02\a0\d2\ac\a8\04\7c\90\ed\da\e7\04\00\01\00\01\01\01\02\01\03\01\04\00" + == "(opt record { head = 1; tail = opt record { head = 2; tail = opt record { head = 3; tail = opt record { head = 4; tail = null }}}})" : (List) "record: list"; + +assert blob "DIDL\02\6e\01\6c\02\a0\d2\ac\a8\04\7c\90\ed\da\e7\04\00\01\00\01\01\01\02\00" + == blob "DIDL\02\6c\02\a0\d2\ac\a8\04\7c\90\ed\da\e7\04\01\6e\00\01\01\01\01\01\02\00" : (List) "record: reorder type table"; + +assert blob "DIDL\02\6e\01\6c\02\a0\d2\ac\a8\04\7c\90\ed\da\e7\04\00\01\00\01\01\01\02\00" + == "(opt record { head = 1; tail = opt record { head = 2; tail = null } })" : (List1) "record: mutual recursive list"; + +assert blob "DIDL\02\6b\02\d1\a7\cf\02\7f\f1\f3\92\8e\04\01\6c\02\a0\d2\ac\a8\04\7c\90\ed\da\e7\04\00\01\00\00" + == "(variant {nil})" : (VariantList) "variant: empty list"; + +assert blob "DIDL\02\6b\02\d1\a7\cf\02\7f\f1\f3\92\8e\04\01\6c\02\a0\d2\ac\a8\04\7c\90\ed\da\e7\04\00\01\00\01\01\01\02\00" + == "(variant { cons = record { head = 1; tail = variant { cons = record { head = 2; tail = variant { nil } } } } })" : (VariantList) "variant: list"; + +assert blob "DIDL\02\6b\02\d1\a7\cf\02\7f\f1\f3\92\8e\04\01\6c\02\a0\d2\ac\a8\04\7c\90\ed\da\e7\04\00\01\00\00" + == "(variant {nil}, null, null, null, null)" : (VariantList, opt VariantList, null, reserved, opt int) "variant: extra args"; + +assert blob "DIDL\02\6b\02\d1\a7\cf\02\7f\f1\f3\92\8e\04\01\6c\02\a0\d2\ac\a8\04\7c\90\ed\da\e7\04\00\01\00\00" !: (VariantList, opt int, vec int) "non-null extra args"; + +// skip fields +assert blob "DIDL\07\6c\07\c3\e3\aa\02\01\d3\e3\aa\02\7e\d5\e3\aa\02\02\db\e3\aa\02\01\a2\e5\aa\02\04\bb\f1\aa\02\06\86\8e\b7\02\75\6c\02\d3\e3\aa\02\7e\86\8e\b7\02\75\6b\02\d1\a7\cf\02\7f\f1\f3\92\8e\04\03\6c\02\a0\d2\ac\a8\04\7c\90\ed\da\e7\04\02\6e\05\6c\02\a0\d2\ac\a8\04\7c\90\ed\da\e7\04\04\6b\02\d3\e3\aa\02\7f\86\8e\b7\02\7f\01\00\01\0b\00\00\00\01\00\00\0a\00\00\00\01\14\00\00\2a\00\00\00" == "(record { foo = 42; bar = true; bat = variant { nil }; baz = record { foo = 10; bar = false }; bbb = opt record { head = 20; tail = null }; bib = variant { bar }; bab = record { foo = 11; bar = true}})" : (A2); + +assert blob "DIDL\07\6c\07\c3\e3\aa\02\01\d3\e3\aa\02\7e\d5\e3\aa\02\02\db\e3\aa\02\01\a2\e5\aa\02\04\bb\f1\aa\02\06\86\8e\b7\02\75\6c\02\d3\e3\aa\02\7e\86\8e\b7\02\75\6b\02\d1\a7\cf\02\7f\f1\f3\92\8e\04\03\6c\02\a0\d2\ac\a8\04\7c\90\ed\da\e7\04\02\6e\05\6c\02\a0\d2\ac\a8\04\7c\90\ed\da\e7\04\04\6b\02\d3\e3\aa\02\7f\86\8e\b7\02\7f\01\00\01\0b\00\00\00\01\00\00\0a\00\00\00\01\14\00\00\2a\00\00\00" == "(record { foo = 42; bar = true })" : (A1) "skip fields"; + +assert blob "DIDL\07\6c\07\c3\e3\aa\02\01\d3\e3\aa\02\7e\d5\e3\aa\02\02\db\e3\aa\02\01\a2\e5\aa\02\04\bb\f1\aa\02\06\86\8e\b7\02\75\6c\02\d3\e3\aa\02\7e\86\8e\b7\02\75\6b\02\d1\a7\cf\02\7f\f1\f3\92\8e\04\03\6c\02\a0\d2\ac\a8\04\7c\90\ed\da\e7\04\02\6e\05\6c\02\a0\d2\ac\a8\04\7c\90\ed\da\e7\04\04\6b\02\d3\e3\aa\02\7f\86\8e\b7\02\7f\01\00\01\0b\00\00\00\01\00\00\0a\00\00\00\01\14\00\00\2a\00\00\00" == "(record { bar = true; baz = record { bar = false }; bbb = opt record { head = 20 }})" : (record { bar: bool; baz: record { bar: bool }; bbb: opt record { head : int} }) "skip fields"; + +assert blob "DIDL\07\6c\07\c3\e3\aa\02\01\d3\e3\aa\02\7e\d5\e3\aa\02\02\db\e3\aa\02\01\a2\e5\aa\02\04\bb\f1\aa\02\06\86\8e\b7\02\75\6c\02\d3\e3\aa\02\7e\86\8e\b7\02\75\6b\02\d1\a7\cf\02\7f\f1\f3\92\8e\04\03\6c\02\a0\d2\ac\a8\04\7c\90\ed\da\e7\04\02\6e\05\6c\02\a0\d2\ac\a8\04\7c\90\ed\da\e7\04\04\6b\02\d3\e3\aa\02\7f\86\8e\b7\02\7f\01\00\01\0b\00\00\00\01\00\00\0a\00\00\00\01\14\00\00\2a\00\00\00" == "(record { bat = variant { nil } })" : (record { bat: variant { nil; cons: record { head : int; tail : VariantList }; new_field: empty } }) "new variant field"; + +assert blob "DIDL\07\6c\07\c3\e3\aa\02\01\d3\e3\aa\02\7e\d5\e3\aa\02\02\db\e3\aa\02\01\a2\e5\aa\02\04\bb\f1\aa\02\06\86\8e\b7\02\75\6c\02\d3\e3\aa\02\7e\86\8e\b7\02\75\6b\02\d1\a7\cf\02\7f\f1\f3\92\8e\04\03\6c\02\a0\d2\ac\a8\04\7c\90\ed\da\e7\04\02\6e\05\6c\02\a0\d2\ac\a8\04\7c\90\ed\da\e7\04\04\6b\02\d3\e3\aa\02\7f\86\8e\b7\02\7f\01\00\01\0b\00\00\00\01\00\00\0a\00\00\00\01\14\00\00\2a\00\00\00" !: (record { foo: int; new_field: bool }) "new record field"; + +// Future types +// This uses 0x67 for the “future type”; bump (well, decrement) once that +// becomes a concrete future type +assert blob "DIDL\01\67\00\02\00\7e\00\00\01" == "(null, true)" : (opt empty,bool) "skipping minimal future type"; +assert blob "DIDL\01\67\03ABC\02\00\7e\05\00hello\01" == "(null,true)" : (opt empty,bool) "skipping future type with data"; diff --git a/test/overshoot.test.did b/test/overshoot.test.did new file mode 100644 index 000000000..46a29a48b --- /dev/null +++ b/test/overshoot.test.did @@ -0,0 +1,32 @@ +/* +Corresponding to spec version version 0.1.6 + +The tests in this file involve large leb-encoded values that indicate sizes (of +tables, arrays, blobs, etc.). In all cases, a decode should be able to catch +that eary by noticing that the overall input is too short to potentially +contain that number of elements. + +The number is always one billion (leb "\80\94\eb\dc\03"). A test suite could run +these tests with a memory limit, of, say 100MB and should still never run into +out of memory conditions, if these eager plausability checks are performed. +*/ + + +assert blob "DIDL\80\94\eb\dc\03\00" !: () "type table length"; +assert blob "DIDL\00\80\94\eb\dc\03" !: () "argument sequence length"; +assert blob "DIDL\00\01\71\80\94\eb\dc\03Motoko" !: (text) "text length"; +assert blob "DIDL\00\01\68\01\80\94\eb\dc\03Motoko" !: (principal) "principal length"; + +assert blob "DIDL\01\6c\80\94\eb\dc\03\00\7f\00\7f" !: () "record field number"; +assert blob "DIDL\01\6b\80\94\eb\dc\03\00\7f\00\7f" !: () "variant field number"; + + +// the next one is an interesting one: it would not work with (vec null) or +// (vec reserved) or similar, as values of these types have size 0. +// Some implementations may detect the overshoot eagerly, others not. +assert blob "DIDL\01\6d\7e\01\00\80\94\eb\dc\03\00\00\00" !: (vec bool) "vector length"; + +assert blob "DIDL\01\6a\68\68\68\68\68\00\00\00\00\00\00\00\00\00\00\00\00\00\00\68\68\68\68\68\68\00\68\68\7a\68\68\68\68\68\68\68\68\68\68\68\68\68\68\68\79\79\79\79\79\79\79\79\79\79\79\79\79\79\79\7a\79\79\79\79\79\79\79\79\79\7b\79\79\79\79\79\7f\00\79\79\79\79\79\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\04\00\00\00\00\01\01\68\68\1d\00\00\00\00\00\00\00\68\1f\00\00\00\00\00\00\00\00\68\00\44\44\44\44\44\44\49\44\4c\00\f7\01\7c\80\80\80\80\80\80\80\80\ff\ff\ff\ff\80\80\80\80\80\80\80\80\ff\ff\ff\ff\80\80\80\80\80\80\80\80\80\80\80\80\49\44\4c\01\6c\01\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\ff\01" !: () "func arg length"; + +assert blob "DIDL\01\67\80\94\eb\dc\03\00\00" !: () "future type length"; +assert blob "DIDL\01\67\00\01\00\80\94\eb\dc\03\00\00" !: () "future value length"; diff --git a/test/prim.test.did b/test/prim.test.did new file mode 100644 index 000000000..bfd3a4b91 --- /dev/null +++ b/test/prim.test.did @@ -0,0 +1,200 @@ +/* +Encoding tests for primitive types + +Corresponding to spec version version 0.1.6 +*/ + +// fundamentally wrong +assert blob "" !: () "empty"; +assert blob "\00\00" !: () "no magic bytes"; +assert blob "DADL" !: () "wrong magic bytes"; +assert blob "DADL\00\00" !: () "wrong magic bytes"; +assert blob "DIDL\80\00\00" : () "overlong typ table length"; +assert blob "DIDL\00\80\00" : () "overlong arg length"; + +// nullary input +assert blob "DIDL\00\00" : (); +assert blob "DIDL\00\00\00" !: () "nullary: too long"; +assert blob "DIDL\00\01\7f" : () "Additional parameters are ignored"; +assert blob "DIDL\00\01\6e" !: () "Not a primitive type"; +assert blob "DIDL\00\01\5e" !: () "Out of range type"; + +// Missing arguments +assert blob "DIDL\00\00" !: (nat) "missing argument: nat fails"; +assert blob "DIDL\00\00" !: (empty) "missing argument: empty fails"; +assert blob "DIDL\00\00" == "(null)" : (null) "missing argument: null"; +assert blob "DIDL\00\00" == "(null)" : (opt empty) "missing argument: opt empty"; +assert blob "DIDL\00\00" == "(null)" : (opt null) "missing argument: opt null"; +assert blob "DIDL\00\00" == "(null)" : (opt nat) "missing argument: opt nat"; +assert blob "DIDL\00\00" == blob "DIDL\00\01\70" : (reserved) "missing argument: reserved"; + +// primitive types +assert blob "DIDL\00\01\7f" : (null); +assert blob "DIDL\00\01\7e" !: (null) "wrong type"; +assert blob "DIDL\00\01\7f\00" !: (null) "null: too long"; + +assert blob "DIDL\00\01\7e\00" == "(false)" : (bool) "bool: false"; +assert blob "DIDL\00\01\7e\01" == "(true)" : (bool) "bool: true"; +assert blob "DIDL\00\01\7e" !: (bool) "bool: missing"; +assert blob "DIDL\00\01\7e\02" !: (bool) "bool: out of range"; +assert blob "DIDL\00\01\7e\ff" !: (bool) "bool: out of range"; + +assert blob "DIDL\00\01\7d\00" == "(0)" : (nat) "nat: 0"; +assert blob "DIDL\00\01\7d\01" == "(1)" : (nat) "nat: 1"; +assert blob "DIDL\00\01\7d\7f" == "(127)" : (nat) "nat: 0x7f"; +assert blob "DIDL\00\01\7d\80\01" == "(128)" : (nat) "nat: leb (two bytes)"; +assert blob "DIDL\00\01\7d\ff\7f" == "(16383)" : (nat) "nat: leb (two bytes, all bits)"; +assert blob "DIDL\00\01\7d\80" !: (nat) "nat: leb too short"; +assert blob "DIDL\00\01\7d\80\00" == "(0)" : (nat) "nat: leb overlong"; +assert blob "DIDL\00\01\7d\ff\00" == "(127)" : (nat) "nat: leb overlong"; +assert blob "DIDL\00\01\7d\80\80\98\f4\e9\b5\ca\6a" == "(60000000000000000)" : (nat) "nat: big number"; + +assert blob "DIDL\00\01\7c\00" == "(0)" : (int) "int: 0"; +assert blob "DIDL\00\01\7c\01" == "(1)" : (int) "int: 1"; +assert blob "DIDL\00\01\7c\7f" == "(-1)" : (int) "int: -1"; +assert blob "DIDL\00\01\7c\40" == "(-64)" : (int) "int: -64"; +assert blob "DIDL\00\01\7c\80\01" == "(128)" : (int) "int: leb (two bytes)"; +assert blob "DIDL\00\01\7c\80" !: (int) "int: leb too short"; +assert blob "DIDL\00\01\7c\80\00" == "(0)" : (int) "int: leb overlong (0s)"; +assert blob "DIDL\00\01\7c\ff\7f" == "(-1)" : (int) "int: leb overlong (1s)"; +assert blob "DIDL\00\01\7c\ff\00" == "(127)" : (int) "int: leb not overlong when signed"; +assert blob "DIDL\00\01\7c\80\7f" == "(-128)" : (int) "int: leb not overlong when signed"; +assert blob "DIDL\00\01\7c\80\80\98\f4\e9\b5\ca\ea\00" == "(60000000000000000)" : (int) "int: big number"; +assert blob "DIDL\00\01\7c\80\80\e8\8b\96\ca\b5\95\7f" == "(-60000000000000000)" : (int) "int: negative big number"; + +assert blob "DIDL\00\01\7d\00" == "(0)" : (int) "nat <: int: 0"; +assert blob "DIDL\00\01\7d\01" == "(1)" : (int) "nat <: int: 1"; +assert blob "DIDL\00\01\7d\7f" == "(127)" : (int) "nat <: int: leb (two bytes)"; +assert blob "DIDL\00\01\7d\80\01" == "(128)" : (int) "nat <: int: leb (two bytes)"; +assert blob "DIDL\00\01\7d\ff\7f" == "(16383)" : (int) "nat <: int: leb (two bytes, all bits)"; + +assert blob "DIDL\00\01\7b\00" == "(0)" : (nat8) "nat8: 0"; +assert blob "DIDL\00\01\7b\01" == "(1)" : (nat8) "nat8: 1"; +assert blob "DIDL\00\01\7b\ff" == "(255)" : (nat8) "nat8: 255"; +assert blob "DIDL\00\01\7b" !: (nat8) "nat8: too short"; +assert blob "DIDL\00\01\7b\00\00" !: (nat8) "nat8: too long"; + +assert blob "DIDL\00\01\7a\00\00" == "(0)" : (nat16) "nat16: 0"; +assert blob "DIDL\00\01\7a\01\00" == "(1)" : (nat16) "nat16: 1"; +assert blob "DIDL\00\01\7a\ff\00" == "(255)" : (nat16) "nat16: 255"; +assert blob "DIDL\00\01\7a\00\01" == "(256)" : (nat16) "nat16: 256"; +assert blob "DIDL\00\01\7a\ff\ff" == "(65535)" : (nat16) "nat16: 65535"; +assert blob "DIDL\00\01\7a" !: (nat16) "nat16: too short"; +assert blob "DIDL\00\01\7a\00" !: (nat16) "nat16: too short"; +assert blob "DIDL\00\01\7a\00\00\00" !: (nat16) "nat16: too long"; + +assert blob "DIDL\00\01\79\00\00\00\00" == "(0)" : (nat32) "nat32: 0"; +assert blob "DIDL\00\01\79\01\00\00\00" == "(1)" : (nat32) "nat32: 1"; +assert blob "DIDL\00\01\79\ff\00\00\00" == "(255)" : (nat32) "nat32: 255"; +assert blob "DIDL\00\01\79\00\01\00\00" == "(256)" : (nat32) "nat32: 256"; +assert blob "DIDL\00\01\79\ff\ff\00\00" == "(65535)" : (nat32) "nat32: 65535"; +assert blob "DIDL\00\01\79\ff\ff\ff\ff" == "(4294967295)" : (nat32) "nat32: 4294967295"; +assert blob "DIDL\00\01\79" !: (nat32) "nat32: too short"; +assert blob "DIDL\00\01\79\00" !: (nat32) "nat32: too short"; +assert blob "DIDL\00\01\79\00\00" !: (nat32) "nat32: too short"; +assert blob "DIDL\00\01\79\00\00\00" !: (nat32) "nat32: too short"; +assert blob "DIDL\00\01\79\00\00\00\00\00" !: (nat32) "nat32: too long"; + +assert blob "DIDL\00\01\78\00\00\00\00\00\00\00\00" == "(0)" : (nat64) "nat64: 0"; +assert blob "DIDL\00\01\78\01\00\00\00\00\00\00\00" == "(1)" : (nat64) "nat64: 1"; +assert blob "DIDL\00\01\78\ff\00\00\00\00\00\00\00" == "(255)" : (nat64) "nat64: 255"; +assert blob "DIDL\00\01\78\00\01\00\00\00\00\00\00" == "(256)" : (nat64) "nat64: 256"; +assert blob "DIDL\00\01\78\ff\ff\00\00\00\00\00\00" == "(65535)" : (nat64) "nat64: 65535"; +assert blob "DIDL\00\01\78\ff\ff\ff\ff\00\00\00\00" == "(4294967295)" : (nat64) "nat64: 4294967295"; +assert blob "DIDL\00\01\78\ff\ff\ff\ff\ff\ff\ff\ff" == "(18446744073709551615)" : (nat64) "nat64: 18446744073709551615"; +assert blob "DIDL\00\01\78" !: (nat64) "nat64: too short"; +assert blob "DIDL\00\01\78\00" !: (nat64) "nat64: too short"; +assert blob "DIDL\00\01\78\00\00" !: (nat64) "nat64: too short"; +assert blob "DIDL\00\01\78\00\00\00" !: (nat64) "nat64: too short"; +assert blob "DIDL\00\01\78\00\00\00\00" !: (nat64) "nat64: too short"; +assert blob "DIDL\00\01\78\00\00\00\00\00" !: (nat64) "nat64: too short"; +assert blob "DIDL\00\01\78\00\00\00\00\00\00" !: (nat64) "nat64: too short"; +assert blob "DIDL\00\01\78\00\00\00\00\00\00\00" !: (nat64) "nat64: too short"; +assert blob "DIDL\00\01\78\00\00\00\00\00\00\00\00\00" !: (nat64) "nat64: too long"; + +assert blob "DIDL\00\01\77\00" == "(0)" : (int8) "int8: 0"; +assert blob "DIDL\00\01\77\01" == "(1)" : (int8) "int8: 1"; +assert blob "DIDL\00\01\77\ff" == "(-1)" : (int8) "int8: -1"; +assert blob "DIDL\00\01\77" !: (int8) "int8: too short"; +assert blob "DIDL\00\01\77\00\00" !: (int8) "int8: too long"; + +assert blob "DIDL\00\01\76\00\00" == "(0)" : (int16) "int16: 0"; +assert blob "DIDL\00\01\76\01\00" == "(1)" : (int16) "int16: 1"; +assert blob "DIDL\00\01\76\ff\00" == "(255)" : (int16) "int16: 255"; +assert blob "DIDL\00\01\76\00\01" == "(256)" : (int16) "int16: 256"; +assert blob "DIDL\00\01\76\ff\ff" == "(-1)" : (int16) "int16: -1"; +assert blob "DIDL\00\01\76" !: (int16) "int16: too short"; +assert blob "DIDL\00\01\76\00" !: (int16) "int16: too short"; +assert blob "DIDL\00\01\76\00\00\00" !: (int16) "int16: too long"; + +assert blob "DIDL\00\01\75\00\00\00\00" == "(0)" : (int32) "int32: 0"; +assert blob "DIDL\00\01\75\01\00\00\00" == "(1)" : (int32) "int32: 1"; +assert blob "DIDL\00\01\75\ff\00\00\00" == "(255)" : (int32) "int32: 255"; +assert blob "DIDL\00\01\75\00\01\00\00" == "(256)" : (int32) "int32: 256"; +assert blob "DIDL\00\01\75\ff\ff\00\00" == "(65535)" : (int32) "int32: 65535"; +assert blob "DIDL\00\01\75\ff\ff\ff\ff" == "(-1)" : (int32) "int32: -1"; +assert blob "DIDL\00\01\75" !: (int32) "int32: too short"; +assert blob "DIDL\00\01\75\00" !: (int32) "int32: too short"; +assert blob "DIDL\00\01\75\00\00" !: (int32) "int32: too short"; +assert blob "DIDL\00\01\75\00\00\00" !: (int32) "int32: too short"; +assert blob "DIDL\00\01\75\00\00\00\00\00" !: (int32) "int32: too long"; + +assert blob "DIDL\00\01\74\00\00\00\00\00\00\00\00" == "(0)" : (int64) "int64: 0"; +assert blob "DIDL\00\01\74\01\00\00\00\00\00\00\00" == "(1)" : (int64) "int64: 1"; +assert blob "DIDL\00\01\74\ff\00\00\00\00\00\00\00" == "(255)" : (int64) "int64: 255"; +assert blob "DIDL\00\01\74\00\01\00\00\00\00\00\00" == "(256)" : (int64) "int64: 256"; +assert blob "DIDL\00\01\74\ff\ff\00\00\00\00\00\00" == "(65535)" : (int64) "int64: 65535"; +assert blob "DIDL\00\01\74\ff\ff\ff\ff\00\00\00\00" == "(4294967295)" : (int64) "int64: 4294967295"; +assert blob "DIDL\00\01\74\ff\ff\ff\ff\ff\ff\ff\ff" == "(-1)" : (int64) "int64: -1"; +assert blob "DIDL\00\01\74" !: (int64) "int64: too short"; +assert blob "DIDL\00\01\74\00" !: (int64) "int64: too short"; +assert blob "DIDL\00\01\74\00\00" !: (int64) "int64: too short"; +assert blob "DIDL\00\01\74\00\00\00" !: (int64) "int64: too short"; +assert blob "DIDL\00\01\74\00\00\00\00" !: (int64) "int64: too short"; +assert blob "DIDL\00\01\74\00\00\00\00\00" !: (int64) "int64: too short"; +assert blob "DIDL\00\01\74\00\00\00\00\00\00" !: (int64) "int64: too short"; +assert blob "DIDL\00\01\74\00\00\00\00\00\00\00" !: (int64) "int64: too short"; +assert blob "DIDL\00\01\74\00\00\00\00\00\00\00\00\00" !: (int64) "int64: too long"; + +assert blob "DIDL\00\01\73\00\00\00\00" == "(0.)" : (float32) "float32: 0"; +assert blob "DIDL\00\01\73\00\00\40\40" == "(3.)" : (float32) "float32: 3"; +assert blob "DIDL\00\01\73\00\00\00\3f" == "(0.5)" : (float32) "float32: 0.5"; +assert blob "DIDL\00\01\73\00\00\00\bf" == "(-0.5)" : (float32) "float32: -0.5"; +assert blob "DIDL\00\01\73\00\00" !: (float32) "float32: too short"; +assert blob "DIDL\00\01\73\00\00\00\00\00" !: (float32) "float32: too long"; + +assert blob "DIDL\00\01\72\00\00\00\00\00\00\00\00" == "(0.)" : (float64) "float64: 0"; +assert blob "DIDL\00\01\72\00\00\00\00\00\00\08\40" == "(3.)" : (float64) "float64: 3"; +assert blob "DIDL\00\01\72\00\00\00\00\00\00\e0\3f" == "(0.5)" : (float64) "float64: 0.5"; +assert blob "DIDL\00\01\72\00\00\00\00\00\00\e0\bf" == "(-0.5)" : (float64) "float64: -0.5"; +assert blob "DIDL\00\01\72\01\00\00\00\00\00\f0\7f" : (float64) "float64: NaN"; +assert blob "DIDL\00\01\72\ff\ff\ff\ff\ff\ff\ef\7f" : (float64) "float64: max value"; +assert blob "DIDL\00\01\72\00\00\00\00" !: (float64) "float64: too short"; +assert blob "DIDL\00\01\72\00\00\00\00\00\00\00\00\00" !: (float64) "float64: too long"; + +assert blob "DIDL\00\01\71\00" == "(\"\")" : (text) "text: empty string"; +assert blob "DIDL\00\01\71\06Motoko" == "(\"Motoko\")" : (text) "text: Motoko"; +assert blob "DIDL\00\01\71\05Motoko" !: (text) "text: too long"; +assert blob "DIDL\00\01\71\07Motoko" !: (text) "text: too short"; +assert blob "DIDL\00\01\71\86\00Motoko" : (text) "text: overlong length leb"; +assert blob "DIDL\00\01\71\03\e2\98\83" == "(\"☃\")" : (text) "text: Unicode"; +assert "(\"\\u{2603}\")" == "(\"☃\")" : (text) "text: Unicode escape"; +assert "(\"\\u{26_03}\")" == "(\"☃\")" : (text) "text: Unicode escape with underscore"; +assert "(\"\\u{2603\")" !: (text) "text: Unicode escape (unclosed)"; +assert blob "DIDL\00\01\71\03\e2\28\a1" !: (text) "text: Invalid utf8"; +assert blob "DIDL\00\01\71\02\e2\98\83" !: (text) "text: Unicode overshoots"; +assert blob "DIDL\00\01\71\06\09\0A\0D\22\27\5C" == "(\"\\t\\n\\r\\\"\\\'\\\\\")" : (text) "text: Escape sequences"; + +assert blob "DIDL\00\01\70" == blob "DIDL\00\01\7f" : (reserved) "reserved from null"; +assert blob "DIDL\00\01\70" == blob "DIDL\00\01\7e\01" : (reserved) "reserved from bool"; +assert blob "DIDL\00\01\70" == blob "DIDL\00\01\7d\80\01" : (reserved) "reserved from nat"; +assert blob "DIDL\00\01\70" == blob "DIDL\00\01\71\06Motoko" : (reserved) "reserved from text"; +assert blob "DIDL\00\00" : (reserved) "reserved extra value"; +assert blob "DIDL\00\01\71\05Motoko" !: (reserved) "reserved from too short text"; +assert blob "DIDL\00\01\71\03\e2\28\a1" !: (reserved) "reserved from invalid utf8 text"; + +assert blob "DIDL\00\01\6f" !: (empty) "cannot decode empty type"; +assert blob "DIDL\01\6e\6f\01\00\00" == "(null)" : (opt empty) "okay to decode non-empty value"; + +assert blob "DIDL\00\0a\7f\7e\7d\7c\7f\70\7f\7b\7a\79\01\2a\2a\2a\2a\00\2a\00\00\00" + == "(null, true, 42, 42, null, null, null, 42, 42, 42)" : (null, bool, nat, int, null, reserved, null, nat8, nat16, nat32) "multiple arguments"; diff --git a/test/reference.test.did b/test/reference.test.did new file mode 100644 index 000000000..51ff72ea4 --- /dev/null +++ b/test/reference.test.did @@ -0,0 +1,69 @@ +/* +Encoding tests for reference types + +Corresponding to spec version version 0.1.6 +*/ + +// principal +assert blob "DIDL\00\01\68\01\00" == "(principal \"aaaaa-aa\")" : (principal) "principal: ic0"; +assert blob "DIDL\00\01\68\01\03\ca\ff\ee" == "(principal \"w7x7r-cok77-xa\")" : (principal) "principal"; +assert blob "DIDL\00\01\68\01\09\ef\cd\ab\00\00\00\00\00\01" + == "(principal \"2chl6-4hpzw-vqaaa-aaaaa-c\")" : (principal) "principal"; +assert blob "DIDL\00\01\68\01\02\ca\ff" != "(principal \"w7x7r-cok77-xa\")" : (principal) "principal"; +assert blob "DIDL\00\01\68\03\ca\ff\ee" !: (principal) "principal: no tag"; +assert blob "DIDL\00\01\68\01\03\ca\ff" !: (principal) "principal: too short"; +assert blob "DIDL\00\01\68\01\03\ca\ff\ee\ee" !: (principal) "principal: too long"; +assert blob "DIDL\01\68\01\00\01\03\ca\ff\ee" !: (principal) "principal: not construct"; + +// service +assert blob "DIDL\00\01\68\01\03\ca\ff\ee" !: (service {}) "service: not principal"; +assert blob "DIDL\00\01\69\01\03\ca\ff\ee" !: (service {}) "service: not primitive type"; +assert "(principal \"w7x7r-cok77-xa\")" !: (service {}) "service: not principal"; +assert blob "DIDL\01\69\00\01\00\01\03\ca\ff\ee" == "(service \"w7x7r-cok77-xa\")" : (service {}) "service"; +assert blob "DIDL\01\69\00\01\00\01\03\ca\ff\ee" == "(service \"w7x7r-cok77-xa\")" : (service {}) "service"; +assert blob "DIDL\02\6a\01\71\01\7d\00\69\01\03foo\00\01\01\01\03\ca\ff\ee" + == "(service \"w7x7r-cok77-xa\")" : (service { foo : (text) -> (nat) }) "service"; +assert blob "DIDL\02\6a\01\71\01\7d\00\69\02\03foo\00\04foo\32\00\01\01\01\03\ca\ff\ee" + == "(service \"w7x7r-cok77-xa\")" : (service { foo : (text) -> (nat); foo2 : (text) -> (nat) }) "service"; + +assert blob "DIDL\02\6a\01\71\01\7d\00\69\03\03foo\00\04foo\32\00\01\01\01\03\ca\ff\ee" !: (service { foo : (text) -> (nat); foo2 : (text) -> (nat) }) "service: too long"; +assert blob "DIDL\02\6a\01\71\01\7d\00\69\01\03foo\00\04foo\32\00\01\01\01\03\ca\ff\ee" !: (service { foo : (text) -> (nat); foo2 : (text) -> (nat) }) "service: too short"; +assert blob "DIDL\02\6a\01\71\01\7d\00\69\02\04foo\32\00\03foo\00\01\01\01\03\ca\ff\ee" !: (service { foo : (text) -> (nat); foo2 : (text) -> (nat) }) "service: unsorted"; +assert blob "DIDL\02\6a\01\71\01\7d\00\69\02\09foobarbaz\00\06foobar\00\01\01\01\03\ca\ff\ee" !: (service { foobar : (text) -> (nat); foobarbaz : (text) -> (nat) }) "service: unsorted (but sorted by hash)"; +assert blob "DIDL\02\6a\01\71\01\7d\00\69\01\03\e2\28\a1\00\01\01\01\03\ca\ff\ee" !: (reserved) "service: invalid unicode"; +assert blob "DIDL\02\6a\01\71\01\7d\00\69\02\03foo\00\03foo\00\01\01\01\03\ca\ff\ee" !: (service { foo : (text) -> (nat) }) "service: duplicate"; + +assert blob "DIDL\05i\02\03foo\01\04\f0\9f\90\82\02j\00\00\01\02j\01\03\01\04\01\01n|l\00\01\00\01\00" + == "(service \"aaaaa-aa\")" : (service {"🐂":(opt int)->(record{}) query; foo:()->() oneway}) "service: unicode"; +assert blob "DIDL\02\6a\01\71\01\7d\00\69\01\03foo\68\01\01\01\03\ca\ff\ee" !: (service { foo: () -> () }) "service { foo: principal }"; +assert blob "DIDL\02\6e\7e\69\01\03foo\00\01\01\01\03\ca\ff\ee" !: (service { foo: () -> () }) "service { foo: opt bool }"; +assert blob "DIDL\02\69\01\03foo\01\6e\7e\01\01\01\03\ca\ff\ee" !: (service { foo: () -> () }) "service { foo: opt bool }"; + +// function +assert blob "DIDL\01\6a\00\00\00\01\00\01\01\03\ca\ff\ee\01\61" + == "(func \"w7x7r-cok77-xa\".\"a\")" : (func () -> ()) "func: quote name"; +assert blob "DIDL\01\6a\00\00\00\01\00\01\01\03\ca\ff\ee\01\61" + == "(func \"w7x7r-cok77-xa\".a)" : (func () -> ()) "func: non-quote name"; +assert blob "DIDL\01\6a\00\00\00\01\00\01\00\03\ca\ff\ee\01\61" !: (func () -> ()) "func: wrong tag"; +assert blob "DIDL\02j\02|}\01\01\01\01i\00\01\00\01\01\00\04\f0\9f\90\82" + == "(func \"aaaaa-aa\".\"🐂\")" : (func (int,nat) -> (service {}) query) "func: unicode"; +assert blob "DIDL\01\6a\01\68\01\7d\00\01\00\01\01\03\ca\ff\ee\03foo" + == "(func \"w7x7r-cok77-xa\".foo)" : (func (principal) -> (nat)) "func"; +assert blob "DIDL\01\6a\01\71\01\7d\01\01\01\00\01\01\03\ca\ff\ee\03foo" + == "(func \"w7x7r-cok77-xa\".foo)" : (func (text) -> (nat) query) "func: query"; +assert blob "DIDL\01\6a\01\71\01\7d\01\03\01\00\01\01\03\ca\ff\ee\03foo" + == "(func \"w7x7r-cok77-xa\".foo)" : (func (text) -> (nat) composite_query) "func: composite query"; +assert blob "DIDL\01\6a\01\71\01\7d\01\80\01\01\00\01\01\03\ca\ff\ee\03foo" !: (func (text) -> (nat) query) "func: unknown annotation"; +assert blob "DIDL\00\01\6a\01\01\03\ca\ff\ee\01\61" !: (func () -> ()) "func: not primitive"; +assert blob "DIDL\00\01\6a\01\03\ca\ff\ee\01\61" !: (func () -> ()) "func: no tag"; +assert blob "DIDL\01\6a\01\69\01\7d\00\01\00\01\01\03\ca\ff\ee\03foo" !: (func (service {}) -> (nat)) "func: service not in type table"; +assert blob "DIDL\01\6a\01\71\01\7d\01\03\01\00\01\01\03\ca\ff\ee\03foo" !: (func (text) -> (nat)) "func: invalid annotation"; + +// subtype +assert blob "DIDL\01\69\00\01\00\01\03\ca\ff\ee" !: (service { foo : (text) -> (nat) }) "service {} !<: service { foo : (text) -> (nat) }"; +assert blob "DIDL\02\6a\01\71\01\7d\01\01\69\01\03foo\00\01\01\01\03\ca\ff\ee" !: (service { foo : (text) -> (nat) }) "service { foo : (text) -> (nat) query } !<: service { foo : (text) -> (nat) }"; +assert blob "DIDL\02\6a\01\71\01\7d\00\69\01\03foo\00\01\01\01\03\ca\ff\ee" !: (service { foo : (text) -> (nat) query }) "service { foo : (text) -> (nat) } !<: service { foo : (text) -> (nat) query }"; +assert blob "DIDL\02\6a\01\71\01\7d\00\69\01\03foo\00\01\01\01\03\ca\ff\ee" == "(service \"w7x7r-cok77-xa\")" : (service {}) "service { foo : (text) -> (nat) } decodes at service {}"; + +assert blob "DIDL\01\6a\00\00\00\01\00\01\01\03\ca\ff\ee\03foo" !: (func (text) -> (nat)) "func () -> () !<: func (text) -> (nat) }"; +assert blob "DIDL\01\6a\01\71\01\7d\00\01\00\01\01\03\ca\ff\ee\03foo" == "(func \"w7x7r-cok77-xa\".foo)" : (func (text, opt text) -> ()) "func (text) -> (nat) decodes at func (text, opt text) -> () }"; diff --git a/test/spacebomb.test.did b/test/spacebomb.test.did new file mode 100644 index 000000000..1c6110465 --- /dev/null +++ b/test/spacebomb.test.did @@ -0,0 +1,37 @@ + +// Space bomb tests + +// Messages in this test all take a lot of time, memory and stack space to decode. +// With infinite resources, these are all valid Candid messages. +// When using Candid in a resource limited environment, for example one consensus round in a blockchain, +// an implementation with self-metering should reject these messages relatively early +// without going through the whole deserialisation process. + +// \80\94\eb\dc\03 is 1000_000_000 +// \80\ad\e2\04 is 10_000_000 +// \ff\ff\3f is 1_048_575 +// \80\b5\18 is 400_000 + + +// Plain decoding (unused arguments) +assert blob "DIDL\01\6d\7f\01\00\80\94\eb\dc\03" !: () "vec null (extra argument)"; +assert blob "DIDL\01\6d\70\01\00\80\94\eb\dc\03" !: () "vec reserved (extra argument)"; +assert blob "DIDL\04\6c\03\00\7f\01\01\02\02\6c\01\00\70\6c\00\6d\00\01\03\80\94\eb\dc\03" !: () "zero-sized record (extra argument)"; +assert blob "DIDL\02\6d\01\6d\7f\01\00\05\ff\ff\3f\ff\ff\3f\ff\ff\3f\ff\ff\3f\ff\ff\3f" !: () "vec vec null (extra argument)"; +assert blob "DIDL\02\6d\01\6c\00\01\00\80\ad\e2\04" !: () "vec record {} (extra argument)"; +assert blob "DIDL\17\6c\02\01\7f\02\7f\6c\02\01\00\02\00\6c\02\00\01\01\01\6c\02\00\02\01\02\6c\02\00\03\01\03\6c\02\00\04\01\04\6c\02\00\05\01\05\6c\02\00\06\01\06\6c\02\00\07\01\07\6c\02\00\08\01\08\6c\02\00\09\01\09\6c\02\00\0a\01\0a\6c\02\00\0b\01\0b\6c\02\00\0c\01\0c\6c\02\00\0d\02\0d\6c\02\00\0e\01\0e\6c\02\00\0f\01\0f\6c\02\00\10\01\10\6c\02\00\11\01\11\6c\02\00\12\01\12\6c\02\00\13\01\13\6e\14\6d\15\01\16\02\01\01" !: () "vec opt record with 2^20 null (extra argument)"; + +// Decoding to actual type +assert blob "DIDL\01\6d\7f\01\00\80\94\eb\dc\03" !: (vec opt nat) "vec null (not ignored)"; +assert blob "DIDL\01\6d\70\01\00\80\94\eb\dc\03" !: (vec reserved) "vec reserved (not ignored)"; +assert blob "DIDL\04\6c\03\00\7f\01\01\02\02\6c\01\00\70\6c\00\6d\00\01\03\80\94\eb\dc\03" !: (vec record {null;record{reserved};record{}}) "zero-sized record (not ignored)"; +assert blob "DIDL\02\6d\01\6d\7f\01\00\05\ff\ff\3f\ff\ff\3f\ff\ff\3f\ff\ff\3f\ff\ff\3f" !: (vec vec null) "vec vec null (not ignored)"; +assert blob "DIDL\02\6d\01\6c\00\01\00\80\ad\e2\04" !: (vec record {}) "vec record {} (not ignored)"; + +// Decoding under opt +assert blob "DIDL\01\6d\7f\01\00\80\94\eb\dc\03" !: (opt nat) "vec null (subtyping)"; +assert blob "DIDL\01\6d\70\01\00\80\94\eb\dc\03" !: (opt nat) "vec reserved (subtyping)"; +assert blob "DIDL\04\6c\03\00\7f\01\01\02\02\6c\01\00\70\6c\00\6d\00\01\03\80\94\eb\dc\03" !: (opt nat) "zero-sized record (subtyping)"; +assert blob "DIDL\02\6d\01\6d\7f\01\00\05\ff\ff\3f\ff\ff\3f\ff\ff\3f\ff\ff\3f\ff\ff\3f" !: (vec opt nat) "vec vec null (subtyping)"; +assert blob "DIDL\02\6d\01\6c\00\01\00\80\ad\e2\04" !: (opt nat) "vec record {} (subtyping)"; +assert blob "DIDL\17\6c\02\01\7f\02\7f\6c\02\01\00\02\00\6c\02\00\01\01\01\6c\02\00\02\01\02\6c\02\00\03\01\03\6c\02\00\04\01\04\6c\02\00\05\01\05\6c\02\00\06\01\06\6c\02\00\07\01\07\6c\02\00\08\01\08\6c\02\00\09\01\09\6c\02\00\0a\01\0a\6c\02\00\0b\01\0b\6c\02\00\0c\01\0c\6c\02\00\0d\02\0d\6c\02\00\0e\01\0e\6c\02\00\0f\01\0f\6c\02\00\10\01\10\6c\02\00\11\01\11\6c\02\00\12\01\12\6c\02\00\13\01\13\6e\14\6d\15\01\16\05\01\01\01\01\01" !: (vec opt record {}) "vec opt record with 2^20 null (subtyping)"; diff --git a/test/subtypes.test.did b/test/subtypes.test.did new file mode 100644 index 000000000..cd92605a8 --- /dev/null +++ b/test/subtypes.test.did @@ -0,0 +1,167 @@ +/* +Encoding tests for subtype tests in decoders + +Corresponding to spec version version 0.1.6 + +This test file contains tests for the subtype check that decoders are expected +to perform upon references. + +The shortest way to trigger a test for `t1 <: t2` is to pass `(func () -> (t1))` +and decode at type `(opt func () -> (t2))`, and check if the result is a +`reference` or `null`. + +The patterns idioms here are thus + +assert blob "DIDL\01\6a\00\01XX00\01\00\01\01\00\01m" + == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (t2)) "t1 <: t2"; +assert blob "DIDL\01\6a\00\01XX\00\01\00\01\01\00\01m" + == "(null)" : (opt func () -> (t2)) "t1 (t2)) "t1 <: t2"; +assert blob "DIDL\02\6a\00\01\01\00XX\01\00\01\01\00\01m" + == "(null)" : (opt func () -> (t2)) "t1 (null)) "null <: null"; +assert blob "DIDL\01\6a\00\01\7e\00\01\00\01\01\00\01m" + == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (bool)) "bool <: bool"; +assert blob "DIDL\01\6a\00\01\7d\00\01\00\01\01\00\01m" + == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (nat)) "nat <: nat"; +assert blob "DIDL\01\6a\00\01\7c\00\01\00\01\01\00\01m" + == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (int)) "int <: int"; + +// more basic cases +assert blob "DIDL\01\6a\00\01\7d\00\01\00\01\01\00\01m" + == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (int)) "nat <: int"; +assert blob "DIDL\01\6a\00\01\7f\00\01\00\01\01\00\01m" + == "(null)" : (opt func () -> (nat)) "int (nat8)) "nat (nat)) "nat8 (opt bool)) "nat <: opt bool"; +assert blob "DIDL\02\6a\00\01\01\00\6e\7e\01\00\01\01\00\01m" + == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (opt bool)) "opt bool <: opt bool"; +assert blob "DIDL\01\6a\00\01\7e\00\01\00\01\01\00\01m" + == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (opt bool)) "bool <: opt bool"; +assert blob "DIDL\02\6a\00\01\01\00\6e\01\01\00\01\01\00\01m" + == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (opt opt nat)) "µ opt <: opt opt nat"; + +// optional record fields +assert blob "DIDL\02\6a\00\01\01\00\6c\00\01\00\01\01\00\01m" + == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (record {})) "record {} <: record {}"; +assert blob "DIDL\02\6a\00\01\01\00\6c\00\01\00\01\01\00\01m" + == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (record { a : opt empty })) "record {} <: record { a : opt empty }"; +assert blob "DIDL\02\6a\00\01\01\00\6c\00\01\00\01\01\00\01m" + == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (record { a : opt null })) "record {} <: record { a : opt null }"; +assert blob "DIDL\02\6a\00\01\01\00\6c\00\01\00\01\01\00\01m" + == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (record { a : reserved })) "record {} <: record { a : reserved }"; +assert blob "DIDL\02\6a\00\01\01\00\6c\00\01\00\01\01\00\01m" + == "(null)" : (opt func () -> (record { a : empty })) "record {} (record { a : nat })) "record {} (record { a : null })) "record {} <: record { a : null }"; + +// optional func results +assert blob "DIDL\02\6a\00\01\01\00\6a\00\00\00\01\00\01\01\00\01m" + == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (func () -> ())) "func () -> () <: func () -> ()"; +assert blob "DIDL\02\6a\00\01\01\00\6a\00\00\00\01\00\01\01\00\01m" + == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (func () -> (opt empty))) "func () -> () <: func () -> (opt empty)"; +assert blob "DIDL\02\6a\00\01\01\00\6a\00\00\00\01\00\01\01\00\01m" + == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (func () -> (opt null))) "func () -> () <: func () -> (opt null)"; +assert blob "DIDL\02\6a\00\01\01\00\6a\00\00\00\01\00\01\01\00\01m" + == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (func () -> (reserved))) "func () -> () <: func () -> (reserved)"; +assert blob "DIDL\02\6a\00\01\01\00\6a\00\00\00\01\00\01\01\00\01m" + == "(null)" : (opt func () -> (func () -> (empty))) "func () -> () (empty)"; +assert blob "DIDL\02\6a\00\01\01\00\6a\00\00\00\01\00\01\01\00\01m" + == "(null)" : (opt func () -> (func () -> (nat))) "func () -> () (nat)"; +assert blob "DIDL\02\6a\00\01\01\00\6a\00\00\00\01\00\01\01\00\01m" + == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (func () -> (null))) "func () -> () <: func () -> (null)"; + +// optional func arguments +assert blob "DIDL\03\6a\00\01\01\00\6a\01\02\00\00\6e\6f\01\00\01\01\00\01m" + == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (func () -> ())) "func (opt empty) -> () <: func () -> ()"; +assert blob "DIDL\03\6a\00\01\01\00\6a\01\02\00\00\6e\7f\01\00\01\01\00\01m" + == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (func () -> ())) "func (opt null) -> () <: func () -> ()"; +assert blob "DIDL\02\6a\00\01\01\00\6a\01\70\00\00\01\00\01\01\00\01m" + == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (func () -> ())) "func (reserved) -> () <: func () -> ()"; +assert blob "DIDL\02\6a\00\01\01\00\6a\01\6f\00\00\01\00\01\01\00\01m" + == "(null)" : (opt func () -> (func () -> ())) "func (empty) -> () ()"; +assert blob "DIDL\02\6a\00\01\01\00\6a\01\7d\00\00\01\00\01\01\00\01m" + == "(null)" : (opt func () -> (func () -> ())) "func (nat) -> () ()"; +assert blob "DIDL\02\6a\00\01\01\00\6a\01\7f\00\00\01\00\01\01\00\01m" + == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (func () -> ())) "func (null) -> () <: func () -> ()"; + +// variants +assert blob "DIDL\02\6a\00\01\01\00\6b\00\01\00\01\01\00\01m" + == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (variant {})) "variant {} <: variant {}"; +assert blob "DIDL\02\6a\00\01\01\00\6b\00\01\00\01\01\00\01m" + == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (variant {0 : nat})) "variant {} <: variant {0 : nat}"; +assert blob "DIDL\02\6a\00\01\01\00\6b\01\00\7d\01\00\01\01\00\01m" + == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (variant {0 : nat})) "variant {0 : nat} <: variant {0 : nat}"; +assert blob "DIDL\02\6a\00\01\01\00\6b\01\00\7e\01\00\01\01\00\01m" + == "(null)" : (opt func () -> (variant {0 : nat})) "variant {0 : bool} (variant {1 : bool})) "variant {0 : bool} (EmptyRecord)) "(µ record) <: (µ record)"; +//assert blob "DIDL\02\6a\00\01\01\00\6c\01\00\01\01\00\01\01\00\01m" +// == "(null)" : (opt func () -> (empty)) "(µ record) (EmptyRecord)) "empty <: (µ record)"; +assert blob "DIDL\02\6a\00\01\01\00\6c\01\00\01\01\00\01\01\00\01m" + == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (record {EmptyRecord})) "(µ record) <: record {µ record}"; +assert blob "DIDL\03\6a\00\01\01\00\6c\01\00\02\6c\01\00\02\01\00\01\01\00\01m" + == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (EmptyRecord)) "record {µ record} <: (µ record)"; +assert blob "DIDL\02\6a\00\01\01\00\6c\01\00\01\01\00\01\01\00\01m" + == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (MuRecordOpt)) "(µ record) <: (μ (record opt))"; + +assert blob "DIDL\02\6a\00\01\01\00\6b\01\00\01\01\00\01\01\00\01m" + == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (EmptyVariant)) "(µ variant) <: (µ variant)"; +assert blob "DIDL\02\6a\00\01\01\00\6b\01\00\01\01\00\01\01\00\01m" + == "(null)" : (opt func () -> (empty)) "(µ variant) (EmptyVariant)) "empty <: (µ variant)"; +assert blob "DIDL\02\6a\00\01\01\00\6b\01\00\01\01\00\01\01\00\01m" + == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (variant {0 : EmptyVariant})) "(µ variant) <: variant {µ variant}"; +assert blob "DIDL\03\6a\00\01\01\00\6b\01\00\02\6b\01\00\02\01\00\01\01\00\01m" + == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (EmptyVariant)) "variant {µ variant} <: (µ variant)"; + +assert blob "DIDL\02\6a\00\01\01\00\6d\01\01\00\01\01\00\01m" + == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (Vec)) "(µ vec) <: (µ vec)"; +assert blob "DIDL\02\6a\00\01\01\00\6d\01\01\00\01\01\00\01m" + == "(null)" : (opt func () -> (empty)) "(µ vec) (Vec)) "empty <: (µ vec)"; +assert blob "DIDL\02\6a\00\01\01\00\6d\01\01\00\01\01\00\01m" + == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (vec Vec)) "(µ vec) <: vec {µ vec}"; +assert blob "DIDL\03\6a\00\01\01\00\6d\02\6d\02\01\00\01\01\00\01m" + == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (Vec)) "vec {µ vec} <: (µ vec)"; + +// future types +// This uses 0x67 for the “future type”; bump (well, decrement) once that +// becomes a concrete future type + +assert blob "DIDL\02\6a\00\01\01\00\67\00\01\00\01\01\00\01m" + == "(opt func \"aaaaa-aa\".m)" : (opt func () -> (opt empty)) "(future type) <: (opt empty)"; +assert blob "DIDL\02\6a\00\01\01\00\67\00\01\00\01\01\00\01m" + == "(null)" : (opt func () -> (nat)) "(future type)