mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-27 05:49:08 +00:00
320 lines
11 KiB
Rust
320 lines
11 KiB
Rust
#![cfg(test)]
|
|
// Even with #[allow(non_snake_case)] on individual idents, rust-analyzer issues diagnostics.
|
|
// See https://github.com/rust-lang/rust-analyzer/issues/6541.
|
|
// For the `v!` macro we use uppercase variables when constructing tag unions.
|
|
#![allow(non_snake_case)]
|
|
|
|
use crate::{
|
|
test_key_eq, test_key_neq,
|
|
util::{check_derivable, check_single_lset_immediate, check_underivable, derive_test},
|
|
v,
|
|
};
|
|
use insta::assert_snapshot;
|
|
use roc_module::symbol::Symbol;
|
|
use roc_types::subs::Variable;
|
|
|
|
use roc_derive_key::{hash::FlatHashKey, DeriveBuiltin::Hash, DeriveError, DeriveKey};
|
|
|
|
test_key_eq! {
|
|
Hash,
|
|
|
|
same_record:
|
|
v!({ a: v!(U8), }), v!({ a: v!(U8), })
|
|
same_record_fields_diff_types:
|
|
v!({ a: v!(U8), }), v!({ a: v!(STR), })
|
|
same_record_fields_any_order:
|
|
v!({ a: v!(U8), b: v!(U8), c: v!(U8), }),
|
|
v!({ c: v!(U8), a: v!(U8), b: v!(U8), })
|
|
explicit_empty_record_and_implicit_empty_record:
|
|
v!(EMPTY_RECORD), v!({})
|
|
|
|
same_tuple:
|
|
v!((v!(U8), v!(U16),)), v!((v!(U8), v!(U16),))
|
|
same_tuple_fields_diff_types:
|
|
v!((v!(U8), v!(U16),)), v!((v!(U32), v!(U64),))
|
|
|
|
same_tag_union:
|
|
v!([ A v!(U8) v!(STR), B v!(STR) ]), v!([ A v!(U8) v!(STR), B v!(STR) ])
|
|
same_tag_union_tags_diff_types:
|
|
v!([ A v!(U8) v!(U8), B v!(U8) ]), v!([ A v!(STR) v!(STR), B v!(STR) ])
|
|
same_tag_union_tags_any_order:
|
|
v!([ A v!(U8) v!(U8), B v!(U8), C ]), v!([ C, B v!(STR), A v!(STR) v!(STR) ])
|
|
explicit_empty_tag_union_and_implicit_empty_tag_union:
|
|
v!(EMPTY_TAG_UNION), v!([])
|
|
|
|
same_recursive_tag_union:
|
|
v!([ Nil, Cons v!(^lst)] as lst), v!([ Nil, Cons v!(^lst)] as lst)
|
|
same_tag_union_and_recursive_tag_union_fields:
|
|
v!([ Nil, Cons v!(STR)]), v!([ Nil, Cons v!(^lst)] as lst)
|
|
}
|
|
|
|
test_key_neq! {
|
|
Hash,
|
|
|
|
different_record_fields:
|
|
v!({ a: v!(U8), }), v!({ b: v!(U8), })
|
|
record_empty_vs_nonempty:
|
|
v!(EMPTY_RECORD), v!({ a: v!(U8), })
|
|
|
|
different_tuple_arities:
|
|
v!((v!(U8), v!(U16),)), v!((v!(U8), v!(U16), v!(U32),))
|
|
|
|
different_tag_union_tags:
|
|
v!([ A v!(U8) ]), v!([ B v!(U8) ])
|
|
tag_union_empty_vs_nonempty:
|
|
v!(EMPTY_TAG_UNION), v!([ B v!(U8) ])
|
|
different_recursive_tag_union_tags:
|
|
v!([ Nil, Cons v!(^lst) ] as lst), v!([ Nil, Next v!(^lst) ] as lst)
|
|
}
|
|
|
|
#[test]
|
|
fn immediates() {
|
|
check_single_lset_immediate(Hash, v!(U8), Symbol::HASH_ADD_U8);
|
|
check_single_lset_immediate(Hash, v!(U16), Symbol::HASH_ADD_U16);
|
|
check_single_lset_immediate(Hash, v!(U32), Symbol::HASH_ADD_U32);
|
|
check_single_lset_immediate(Hash, v!(U64), Symbol::HASH_ADD_U64);
|
|
check_single_lset_immediate(Hash, v!(U128), Symbol::HASH_ADD_U128);
|
|
check_single_lset_immediate(Hash, v!(I8), Symbol::HASH_HASH_I8);
|
|
check_single_lset_immediate(Hash, v!(I16), Symbol::HASH_HASH_I16);
|
|
check_single_lset_immediate(Hash, v!(I32), Symbol::HASH_HASH_I32);
|
|
check_single_lset_immediate(Hash, v!(I64), Symbol::HASH_HASH_I64);
|
|
check_single_lset_immediate(Hash, v!(I128), Symbol::HASH_HASH_I128);
|
|
check_single_lset_immediate(Hash, v!(STR), Symbol::HASH_HASH_STR_BYTES);
|
|
check_single_lset_immediate(Hash, v!(Symbol::LIST_LIST v!(U8)), Symbol::HASH_HASH_LIST);
|
|
check_single_lset_immediate(Hash, v!(Symbol::LIST_LIST v!(STR)), Symbol::HASH_HASH_LIST);
|
|
}
|
|
|
|
#[test]
|
|
fn optional_record_field_derive_error() {
|
|
check_underivable(Hash, v!({ ?a: v!(U8), }), DeriveError::Underivable);
|
|
}
|
|
|
|
#[test]
|
|
fn derivable_record_ext_flex_var() {
|
|
check_derivable(
|
|
Hash,
|
|
v!({ a: v!(STR), }* ),
|
|
DeriveKey::Hash(FlatHashKey::Record(vec!["a".into()])),
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn derivable_record_ext_flex_able_var() {
|
|
check_derivable(
|
|
Hash,
|
|
v!({ a: v!(STR), }a implements Symbol::DECODE_DECODER ),
|
|
DeriveKey::Hash(FlatHashKey::Record(vec!["a".into()])),
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn derivable_record_with_record_ext() {
|
|
check_derivable(
|
|
Hash,
|
|
v!({ b: v!(STR), }{ a: v!(STR), } ),
|
|
DeriveKey::Hash(FlatHashKey::Record(vec!["a".into(), "b".into()])),
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn derivable_tag_ext_flex_var() {
|
|
check_derivable(
|
|
Hash,
|
|
v!([ A v!(STR) ]* ),
|
|
DeriveKey::Hash(FlatHashKey::TagUnion(vec![("A".into(), 1)])),
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn derivable_tag_ext_flex_able_var() {
|
|
check_derivable(
|
|
Hash,
|
|
v!([ A v!(STR) ]a implements Symbol::ENCODE_TO_ENCODER),
|
|
DeriveKey::Hash(FlatHashKey::TagUnion(vec![("A".into(), 1)])),
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn derivable_tag_with_tag_ext() {
|
|
check_derivable(
|
|
Hash,
|
|
v!([ B v!(STR) v!(U8) ][ A v!(STR) ]),
|
|
DeriveKey::Hash(FlatHashKey::TagUnion(vec![
|
|
("A".into(), 1),
|
|
("B".into(), 2),
|
|
])),
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn empty_record() {
|
|
derive_test(Hash, v!(EMPTY_RECORD), |golden| {
|
|
assert_snapshot!(golden, @r###"
|
|
# derived for {}
|
|
# hasher, {} -[[hash_{}(0)]]-> hasher where hasher implements Hasher
|
|
# hasher, {} -[[hash_{}(0)]]-> hasher where hasher implements Hasher
|
|
# Specialization lambda sets:
|
|
# @<1>: [[hash_{}(0)]]
|
|
#Derived.hash_{} = \#Derived.hasher, #Derived.rcd -> #Derived.hasher
|
|
"###
|
|
)
|
|
})
|
|
}
|
|
|
|
#[test]
|
|
fn zero_field_record() {
|
|
derive_test(Hash, v!({}), |golden| {
|
|
assert_snapshot!(golden, @r###"
|
|
# derived for {}
|
|
# hasher, {} -[[hash_{}(0)]]-> hasher where hasher implements Hasher
|
|
# hasher, {} -[[hash_{}(0)]]-> hasher where hasher implements Hasher
|
|
# Specialization lambda sets:
|
|
# @<1>: [[hash_{}(0)]]
|
|
#Derived.hash_{} = \#Derived.hasher, #Derived.rcd -> #Derived.hasher
|
|
"###
|
|
)
|
|
})
|
|
}
|
|
|
|
#[test]
|
|
fn one_field_record() {
|
|
derive_test(Hash, v!({ a: v!(U8), }), |golden| {
|
|
assert_snapshot!(golden, @r###"
|
|
# derived for { a : U8 }
|
|
# hasher, { a : a } -[[hash_{a}(0)]]-> hasher where a implements Hash, hasher implements Hasher
|
|
# hasher, { a : a } -[[hash_{a}(0)]]-> hasher where a implements Hash, hasher implements Hasher
|
|
# Specialization lambda sets:
|
|
# @<1>: [[hash_{a}(0)]]
|
|
#Derived.hash_{a} =
|
|
\#Derived.hasher, #Derived.rcd -> hash #Derived.hasher #Derived.rcd.a
|
|
"###
|
|
)
|
|
})
|
|
}
|
|
|
|
#[test]
|
|
fn two_field_record() {
|
|
derive_test(Hash, v!({ a: v!(U8), b: v!(STR), }), |golden| {
|
|
assert_snapshot!(golden, @r###"
|
|
# derived for { a : U8, b : Str }
|
|
# hasher, { a : a, b : a1 } -[[hash_{a,b}(0)]]-> hasher where a implements Hash, a1 implements Hash, hasher implements Hasher
|
|
# hasher, { a : a, b : a1 } -[[hash_{a,b}(0)]]-> hasher where a implements Hash, a1 implements Hash, hasher implements Hasher
|
|
# Specialization lambda sets:
|
|
# @<1>: [[hash_{a,b}(0)]]
|
|
#Derived.hash_{a,b} =
|
|
\#Derived.hasher, #Derived.rcd ->
|
|
hash (hash #Derived.hasher #Derived.rcd.a) #Derived.rcd.b
|
|
"###
|
|
)
|
|
})
|
|
}
|
|
|
|
#[test]
|
|
fn two_element_tuple() {
|
|
derive_test(Hash, v!((v!(U8), v!(STR),)), |golden| {
|
|
assert_snapshot!(golden, @r###"
|
|
# derived for ( U8, Str )*
|
|
# hasher, ( a, a1 )* -[[hash_(arity:2)(0)]]-> hasher where a implements Hash, a1 implements Hash, hasher implements Hasher
|
|
# hasher, ( a, a1 )* -[[hash_(arity:2)(0)]]-> hasher where a implements Hash, a1 implements Hash, hasher implements Hasher
|
|
# Specialization lambda sets:
|
|
# @<1>: [[hash_(arity:2)(0)]]
|
|
#Derived.hash_(arity:2) =
|
|
\#Derived.hasher, #Derived.tup ->
|
|
hash (hash #Derived.hasher #Derived.tup.0) #Derived.tup.1
|
|
"###
|
|
)
|
|
})
|
|
}
|
|
|
|
#[test]
|
|
fn tag_one_label_no_payloads() {
|
|
derive_test(Hash, v!([A]), |golden| {
|
|
assert_snapshot!(golden, @r###"
|
|
# derived for [A]
|
|
# hasher, [A] -[[hash_[A 0](0)]]-> hasher where hasher implements Hasher
|
|
# hasher, [A] -[[hash_[A 0](0)]]-> hasher where hasher implements Hasher
|
|
# Specialization lambda sets:
|
|
# @<1>: [[hash_[A 0](0)]]
|
|
#Derived.hash_[A 0] = \#Derived.hasher, A -> #Derived.hasher
|
|
"###
|
|
)
|
|
})
|
|
}
|
|
|
|
#[test]
|
|
fn tag_one_label_newtype() {
|
|
derive_test(Hash, v!([A v!(U8) v!(STR)]), |golden| {
|
|
assert_snapshot!(golden, @r###"
|
|
# derived for [A U8 Str]
|
|
# hasher, [A a a1] -[[hash_[A 2](0)]]-> hasher where a implements Hash, a1 implements Hash, hasher implements Hasher
|
|
# hasher, [A a a1] -[[hash_[A 2](0)]]-> hasher where a implements Hash, a1 implements Hash, hasher implements Hasher
|
|
# Specialization lambda sets:
|
|
# @<1>: [[hash_[A 2](0)]]
|
|
#Derived.hash_[A 2] =
|
|
\#Derived.hasher, A #Derived.2 #Derived.3 ->
|
|
hash (hash #Derived.hasher #Derived.2) #Derived.3
|
|
"###
|
|
)
|
|
})
|
|
}
|
|
|
|
#[test]
|
|
fn tag_two_labels() {
|
|
derive_test(Hash, v!([A v!(U8) v!(STR) v!(U16), B v!(STR)]), |golden| {
|
|
assert_snapshot!(golden, @r###"
|
|
# derived for [A U8 Str U16, B Str]
|
|
# a, [A a1 a2 a3, B a3] -[[hash_[A 3,B 1](0)]]-> a where a implements Hasher, a1 implements Hash, a2 implements Hash, a3 implements Hash
|
|
# a, [A a1 a2 a3, B a3] -[[hash_[A 3,B 1](0)]]-> a where a implements Hasher, a1 implements Hash, a2 implements Hash, a3 implements Hash
|
|
# Specialization lambda sets:
|
|
# @<1>: [[hash_[A 3,B 1](0)]]
|
|
#Derived.hash_[A 3,B 1] =
|
|
\#Derived.hasher, #Derived.union ->
|
|
when #Derived.union is
|
|
A #Derived.3 #Derived.4 #Derived.5 ->
|
|
hash
|
|
(hash (hash (addU8 #Derived.hasher 0) #Derived.3) #Derived.4)
|
|
#Derived.5
|
|
B #Derived.6 -> hash (addU8 #Derived.hasher 1) #Derived.6
|
|
"###
|
|
)
|
|
})
|
|
}
|
|
|
|
#[test]
|
|
fn tag_two_labels_no_payloads() {
|
|
derive_test(Hash, v!([A, B]), |golden| {
|
|
assert_snapshot!(golden, @r###"
|
|
# derived for [A, B]
|
|
# a, [A, B] -[[hash_[A 0,B 0](0)]]-> a where a implements Hasher
|
|
# a, [A, B] -[[hash_[A 0,B 0](0)]]-> a where a implements Hasher
|
|
# Specialization lambda sets:
|
|
# @<1>: [[hash_[A 0,B 0](0)]]
|
|
#Derived.hash_[A 0,B 0] =
|
|
\#Derived.hasher, #Derived.union ->
|
|
when #Derived.union is
|
|
A -> addU8 #Derived.hasher 0
|
|
B -> addU8 #Derived.hasher 1
|
|
"###
|
|
)
|
|
})
|
|
}
|
|
|
|
#[test]
|
|
fn recursive_tag_union() {
|
|
derive_test(Hash, v!([Nil, Cons v!(U8) v!(^lst) ] as lst), |golden| {
|
|
assert_snapshot!(golden, @r###"
|
|
# derived for [Cons U8 $rec, Nil] as $rec
|
|
# a, [Cons a1 a2, Nil] -[[hash_[Cons 2,Nil 0](0)]]-> a where a implements Hasher, a1 implements Hash, a2 implements Hash
|
|
# a, [Cons a1 a2, Nil] -[[hash_[Cons 2,Nil 0](0)]]-> a where a implements Hasher, a1 implements Hash, a2 implements Hash
|
|
# Specialization lambda sets:
|
|
# @<1>: [[hash_[Cons 2,Nil 0](0)]]
|
|
#Derived.hash_[Cons 2,Nil 0] =
|
|
\#Derived.hasher, #Derived.union ->
|
|
when #Derived.union is
|
|
Cons #Derived.3 #Derived.4 ->
|
|
hash (hash (addU8 #Derived.hasher 0) #Derived.3) #Derived.4
|
|
Nil -> addU8 #Derived.hasher 1
|
|
"###
|
|
)
|
|
})
|
|
}
|