Skip to content

Commit afc7743

Browse files
committed
rust test coverage tool, more functora-tagged tests, ParseError source impl
1 parent 9047aec commit afc7743

File tree

7 files changed

+168
-4
lines changed

7 files changed

+168
-4
lines changed

rust/flake.nix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@
109109
bacon
110110
cargo
111111
cargo-edit
112+
cargo-tarpaulin
112113
clippy
113114
rust-analyzer
114115
rustc

rust/functora-tagged/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
/target
2+
tarpaulin-report.html

rust/functora-tagged/src/parse_error.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,15 @@ impl<Rep, Tag> Error for ParseError<Rep, Tag>
1818
where
1919
Rep: Debug + FromStr,
2020
Tag: Debug + Refine<Rep>,
21-
Rep::Err: Debug + Display,
22-
Tag::RefineError: Debug + Display,
21+
Rep::Err: Error + Debug + Display + 'static,
22+
Tag::RefineError: Error + Debug + Display + 'static,
2323
{
24+
fn source(&self) -> Option<&(dyn Error + 'static)> {
25+
match self {
26+
ParseError::Decode(e) => Some(e),
27+
ParseError::Refine(e) => Some(e),
28+
}
29+
}
2430
}
2531

2632
impl<Rep, Tag> Eq for ParseError<Rep, Tag>

rust/functora-tagged/tests/infallible.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,28 @@ fn test_infallible_into() {
66
let result_ok: Result<i32, Infallible> = Ok(10);
77
assert_eq!(result_ok.infallible(), 10);
88
}
9+
10+
#[test]
11+
fn test_infallible_explicit_call() {
12+
let my_result: Result<u32, Infallible> = Ok(42);
13+
assert_eq!(my_result.infallible(), 42);
14+
}
15+
16+
#[test]
17+
fn test_infallible_trait_usage() {
18+
fn takes_infallible_into<R: InfallibleInto<i32>>(
19+
val: R,
20+
) -> i32 {
21+
val.infallible()
22+
}
23+
let ok_result: Result<i32, Infallible> = Ok(100);
24+
assert_eq!(takes_infallible_into(ok_result), 100);
25+
}
26+
27+
#[test]
28+
fn test_infallible_result_construction() {
29+
let res: Result<String, Infallible> =
30+
Ok("hello".to_string());
31+
assert!(res.is_ok());
32+
assert_eq!(res.infallible(), "hello");
33+
}

rust/functora-tagged/tests/integration.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ use functora_tagged::{
44
};
55
#[cfg(feature = "serde")]
66
use serde::{Deserialize, Serialize};
7+
use std::collections::hash_map::DefaultHasher;
78
use std::convert::Infallible;
89
use std::error::Error;
910
use std::fmt::Debug;
11+
use std::hash::{Hash, Hasher};
1012

1113
#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Debug)]
1214
pub enum NonEmptyTag {}
@@ -177,6 +179,51 @@ fn test_upper_string_infallible() {
177179
assert_eq!(tagged.rep(), "TEST");
178180
}
179181

182+
#[test]
183+
fn test_tagged_display() {
184+
let tagged: NonEmpty<String> =
185+
"display_test".parse().unwrap();
186+
let display_str = format!("{}", tagged);
187+
assert_eq!(display_str, "display_test");
188+
}
189+
190+
#[test]
191+
fn test_tagged_hash() {
192+
let tagged1: NonEmpty<String> =
193+
"hash_test".parse().unwrap();
194+
let tagged2: NonEmpty<String> =
195+
"hash_test".parse().unwrap();
196+
let tagged3: NonEmpty<String> =
197+
"another_hash_test".parse().unwrap();
198+
199+
let mut hasher1 = DefaultHasher::new();
200+
tagged1.hash(&mut hasher1);
201+
let hash1 = hasher1.finish();
202+
203+
let mut hasher2 = DefaultHasher::new();
204+
tagged2.hash(&mut hasher2);
205+
let hash2 = hasher2.finish();
206+
207+
let mut hasher3 = DefaultHasher::new();
208+
tagged3.hash(&mut hasher3);
209+
let hash3 = hasher3.finish();
210+
211+
assert_eq!(hash1, hash2);
212+
assert_ne!(hash1, hash3);
213+
}
214+
215+
#[test]
216+
fn test_tagged_deref() {
217+
let tagged: UpperString = UpperString::new(
218+
"deref_test".into(),
219+
)
220+
.expect(
221+
"This should not fail as Infallible cannot fail",
222+
);
223+
assert_eq!(tagged.to_uppercase(), "DEREF_TEST");
224+
assert_eq!(tagged.len(), 10);
225+
}
226+
180227
#[cfg(feature = "serde")]
181228
#[test]
182229
fn test_serde_user_id_roundtrip() {

rust/functora-tagged/tests/parse_error.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,69 @@ fn test_parse_error_eq() {
102102
assert_eq!(err_refine1, err_refine2);
103103
assert_ne!(err_decode1, err_refine1);
104104
}
105+
106+
#[test]
107+
fn test_parse_error_error_trait() {
108+
let decode_err = "abc".parse::<i32>().unwrap_err();
109+
let parse_error_decode =
110+
TestParseError::Decode(decode_err.clone());
111+
if let Some(source_err) = parse_error_decode.source() {
112+
if let Some(parsed_int_err) =
113+
source_err
114+
.downcast_ref::<std::num::ParseIntError>()
115+
{
116+
assert_eq!(parsed_int_err, &decode_err);
117+
} else {
118+
panic!("Source error was not a ParseIntError");
119+
}
120+
} else {
121+
panic!("Source was None");
122+
}
123+
124+
let refine_err = MyRefineError;
125+
let parse_error_refine =
126+
TestParseError::Refine(refine_err.clone());
127+
if let Some(source_err) = parse_error_refine.source() {
128+
if let Some(my_refine_err) =
129+
source_err.downcast_ref::<MyRefineError>()
130+
{
131+
assert_eq!(my_refine_err, &refine_err);
132+
} else {
133+
panic!("Source error was not a MyRefineError");
134+
}
135+
} else {
136+
panic!("Source was None");
137+
}
138+
}
139+
140+
#[test]
141+
fn test_parse_error_eq_trait() {
142+
let decode_err1 = "abc".parse::<i32>().unwrap_err();
143+
let refine_err1 = MyRefineError;
144+
145+
let err_decode1 =
146+
TestParseError::Decode(decode_err1.clone());
147+
148+
let err_refine1 =
149+
TestParseError::Refine(refine_err1.clone());
150+
151+
assert_eq!(err_decode1, err_decode1);
152+
assert_eq!(err_refine1, err_refine1);
153+
154+
assert_ne!(err_decode1, err_refine1);
155+
}
156+
157+
#[test]
158+
fn test_parse_error_clone_variants() {
159+
let decode_err = "abc".parse::<i32>().unwrap_err();
160+
let parse_error_decode =
161+
TestParseError::Decode(decode_err.clone());
162+
let cloned_decode = parse_error_decode.clone();
163+
assert_eq!(parse_error_decode, cloned_decode);
164+
165+
let refine_err = MyRefineError;
166+
let parse_error_refine =
167+
TestParseError::Refine(refine_err.clone());
168+
let cloned_refine = parse_error_refine.clone();
169+
assert_eq!(parse_error_refine, cloned_refine);
170+
}

rust/functora-tagged/tests/refine.rs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ impl Refine<String> for MyTag {
2222
}
2323

2424
#[test]
25-
fn test_refine_default_implementation() {
25+
fn test_refine_my_tag_implementation() {
2626
let rep_value = String::from("test_string");
2727
let refined_rep = MyTag::refine(rep_value.clone());
2828

@@ -50,7 +50,7 @@ impl Refine<String> for StrictTag {
5050
}
5151

5252
#[test]
53-
fn test_refine_custom_implementation() {
53+
fn test_refine_strict_tag_implementation() {
5454
let strict_value = String::from("strict_value");
5555
let refined_strict =
5656
StrictTag::refine(strict_value.clone());
@@ -69,3 +69,21 @@ fn test_refine_custom_implementation() {
6969
)
7070
);
7171
}
72+
73+
#[derive(Debug)]
74+
struct DefaultRefineTag;
75+
76+
impl Refine<String> for DefaultRefineTag {
77+
type RefineError = String;
78+
}
79+
80+
#[test]
81+
fn test_refine_default_implementation() {
82+
let rep_value = String::from("test_default_refine");
83+
84+
let refined_rep =
85+
DefaultRefineTag::refine(rep_value.clone());
86+
87+
assert!(refined_rep.is_ok());
88+
assert_eq!(refined_rep.unwrap(), rep_value);
89+
}

0 commit comments

Comments
 (0)