How to assert io errors in Rust? - unit-testing

There are plenty of tutorials showing how to write unit tests in Rust. I've read dozen and all of them focuses on asserting values in case of success. Situation doesn't seem so straight forward in case of an error. Errors do not implement PartialEq trait by default so you can't use assert_eq! macro. Also, some functions may return multiple variants of error depending on what kind of issue occurred (ex. io::Error which may be of different kind.) I could just check if error occurred or not but it doesn't seem enough.
Example below.
fn parse_data(input: i32) -> Result<i32, io::Error> {
match input {
0 => Ok(0),
_ => Err(io::Error::new(io::ErrorKind::InvalidData, "unexpected number"))
}
}
#[test]
fn test_parsing_wrong_data() {
let result = parse_data(1);
assert!(result.is_err());
let got = result.unwrap_err();
let want = io::Error::new(io::ErrorKind::InvalidData, "unexpected number");
// compilation error here: binary operation `==` cannot be applied to type `std::io::Error`
assert_eq!(want, got);
}
I assume this is not idiomatic approach, since it's not compiling. Hence the question - what is a proper and idiomatic approach in similar situation?

TL;DR: Error should implement PartialEq
Result<T, E> only implements PartialEq when T and E also implement PartialEq, but io::Error doesn't. alex confirm that cause io::Error takes an extra error that implements dyn Error, allowing the user to add extra information lead std to not implement PartialEq.
I see a lot of answer and comment that seem a lot of peoples use io::Error to create their own error. This is NOT a good practice, io::Error should be used only if you deal yourself with io. There is the Error Handling Project Group if you want to learn and share your view about error in Rust.
For now there is some common crate in Rust to make your own error (feel free to add crate):
snafu (my favorite)
thiserror
anyhow
quick-error
I don't totally agree but here a good guide about Error in Rust.
Anyway, the solution that you probably want in your case is just to compare the ErrorKind value. As ErrorKind implements PartialEq this will compile with assert_eq()
use std::io;
fn parse_data(input: i32) -> Result<i32, io::Error> {
match input {
0 => Ok(0),
x => Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("unexpected number {}", x),
)),
}
}
#[test]
fn test_parsing_wrong_data() {
let result = parse_data(1).map_err(|e| e.kind());
let expected = Err(io::ErrorKind::InvalidData);
assert_eq!(expected, result);
}

I solved it with this
assert!(matches!(result, Err(crate::Error::InvalidType(t)) if t == "foobar"));
This solution doesn't require PartialEq for the Error, but still allows me to compare with the variant contents.
If you don't care about the contents of the variant, then its just
assert!(matches!(result, Err(crate::Error::InvalidType(_))));

In my case I had several structs with differents fields, they all implement the std::error::Error trait. This worked for me in a cleaner way than making an instance of any struct I needed:
let result = parse_data(1);
let your_error = result.unwrap_err().downcast_ref::<MyCustomError>();
assert!(your_error.is_some());
That downcasts the error to a specific type, in case it's the correct type it's a Some(_) value, otherwise, it's None. However, I'm a newbie in Rust, it's probably that this is not the best way to solve your problem.

If you use the type Result,
there is a built in method is_err().
This means you can test with assert!(my_result.is_err());.

A macro using Genaritos answer.
macro_rules! is_error_type {
($result:ident,$err_type:ident) => {
$result.unwrap_err().downcast_ref::<$err_type>().is_some()
}
}
used like this:
let result = parse_data(1);
assert!(is_error_type(result, MyCustomError));

Related

Conversion from ocaml to rescript "Did you forget a `=>` here?" syntax error

Trying to convert this OCaml code to Rescript
let emptyEventHandler = ((((fun _ev -> ())
[#bs ]) : Web.Node.event_cb) : Web.Node.event_cb)
And that is what I have got after the conversion
let emptyEventHandler: Web.Node.event_cb = ((. _ev) => (): Web.Node.event_cb)
the error is Did you forget a `=>` here? (at the end)
Does anyone know if this is caused by the syntax changes they made on Rescript and any idea on how can this be fixed?
The original OCaml has a duplicate type annotation for some reason. This is valid in OCaml because everything is an expression, and every expression can have a type annotation, but is completely redundant. One possible reason for this happening is that the code might have been converted between OCaml and Reason too many times.
Rescript doesn't seem to support this case. It prefers type annotations on bindings and doesn't seem to support type annotations on expressions in general. But since Rescript is still represented by OCaml's AST, which does not make this distinction, it seems it will only take the first type annotation of a binding expression leaving the rest as-is and producing invalid Rescript code.
I'd consider this a bug since I think it should produce an error instead of invalid code, but either way the fix is simple, just remove the extraneous type annotation.
In OCaml it would then become:
let emptyEventHandler = (((fun _ev -> ()) [#bs]) : Web.Node.event_cb)
And in Rescript:
let emptyEventHandler: Web.Node.event_cb = (. _ev) => ()

camlp4 syntax extension, parser error

I created a syntax extension that allow the definition of a type as
type.yjson type_name {
/* type_declaration */
}
to be able to build a record value directly from a json file.
The syntax extension insert a module and the function necessary to do so.
Until here, no problem. The syntax extension do exactly what I wanted.
I start having some issue if I want to use "yjson" at some other place in my code (i.e:function parameter).
Here what I tried:
EXTEND Gram
str_item:
[
[ KEYWORD "type"; KEYWORD "."; "yjson"; tdl_raw = type_declaration ->
Here the error I get when I use "yjson" as a function parameter
[fun_binding] expected after [ipatt] (in [let_binding])
I don't really understand what happen here. It doesn't seems like the rule have been match, so why do I get a parse error ?
I do not perfectly understand the P4's mechanism around this, but [ [ "blahblah" -> ... makes blahblah as a new keyword of the language, so you can no longer use blahblah as a function argument.
To see this, try preprocess your pa_*.ml by camlp4of and see how "blahblah" is expanded to Gram.Skeyword "blahblah". It seems that this Skeyword _ is passed to Structure.using via Insert.insert of P4 and the string is registered as a new keyword.
To keep yjson usable as a normal variable, use id = LIDENT instead of "yjson" in your rule, then check id's content is "yjson" or not in your action.
If I can make a slightly off-topic remark, I think it's wrong to design a custom syntax for type-directed code generation, when there already exist two different syntaxes (one for type_conv and one for deriving), one of which (type-conv) is becoming a de facto standard.
type foo = {
...
} with json
If you pick a syntax for this, you should use this one unless you have very good reasons not to. In fact, type-conv itself is a helper utility to let you write your own type-directed code generators, so you may as well use type-conv directly for what you're trying to do.
(You probably know about Martin Jambon's Atdgen, which made a conscious choice not to use Camlp4; there is ongoing work by Alain Frisch to support annotations directly in the OCaml syntax, but that's not yet ready for consumption.)

OCaml: Throw away return value in imperative code

How do I successfully throw away the return value of a function and treat it as if it's returning unit (for side-effects, obviously). The obvious approach is to do this:
let foo = begin
some_ignored_thing ();
actual_return
end
However, the compiler complains (with a warning) that the return type of some_ignored_thing isn't unit.
I could invent my own method, such as:
let ignore a = ()
Which seems at least concise and flags that something's going on, but is there anything in the language/stdlib that achieves the same thing? I'd have thought it would be a relatively common use case.
Indeed, there is an ignore : 'a -> unit function that does exactly this.
The compiler actually knows about ignore and hard-code a specific behavior that is generally very useful, but can occasionally be inconvenient: by default, ignoring a function (anything with a type of the form foo -> bar) will raise a warning.
The reason for this is that forgetting to add the last argument of a function is a relatively common mistake. You may write for example
ignore (List.map (fun x -> foo bar))
while you meant to write
ignore (List.map (fun x -> foo bar) li)
and the effects you expect to see applied are not.
So ignore will raise a warning in this case, which your hand-coded let ignore x = () does not do. People also sometimes write
let _ = foo in ...
to ignore foo, but this suffer from the same downside (it's easy to be wrong about what the type actually is). Kakadu recommends to use a type annotation there, and that's a reasonable advice.
(Some people also use let _ = ... for the "main" expression of their program. I recommend to rather use let () = ... for this same reason that it forces more type precision.)
Finally, some people prefer to use the let _ = ... form because they are in situations where using ; would force them to add parentheses or begin..end. I recommend that you always add parentheses or begin..end in these cases, because it's easy to shoot yourself in the foot with that -- obscure precedence rules for if..then..else.. for example.

Why doesn't Assert::AreEqual in the VS UnitTesting framework work with std::string?

I am trying to do a unit test on some c++ code but am running into some trouble.
I have something similar to the following lines of code...
std::string s1 = obj->getName();
std::string s2 = "ExpectedName";
Assert::AreEqual(s1, s2, "Unexpected Object Name");
And I'm getting the following compiler error...
error C2665: 'Microsoft::VisualStudio::TestTools::UnitTesting::Assert::AreEqual' :
none of the 15 overloads could convert all the argument types
It seems like it should be a match with the following overload:
AreEqual<(Of <(T>)>)(T, T, String)
Isn't the above overload a template overload that should support any object, as long as arguments 1 and 2 are of the same type? Or am I missing something?
Is there some other way that I can accomplish this Assert?
You're attempting to use the managed unit testing framework with native types – this simply isn't going to work without marshaling the objects into managed types first.
VS2012 now comes with a native C++ unit testing framework; using this framework instead, your code could work by changing "Unexpected Object Name" to a wide string (prefix with L) and calling the following overload:
template<typename T>
static void AreEqual(
const T& expected,
const T& actual,
const wchar_t* message = NULL,
const __LineInfo* pLineInfo = NULL)
If we are trying to stay in un-managed C++, and we don't care what the error message looks like, this is probably a better option than the accepted answer:
Assert::IsTrue(s1==s2)
By better, I mean it is at least easy to read.
I hacked up a bit of a workaround so that integers are compared instead of strings:
Assert::AreEqual(0, s1.compare(s2), "Unexpected Object Name");
In the future, we will likely switch to native C++ unit testing, but in the meantime, this does the trick. Obviously the messaging for this isn't very helpful
Assert.AreEqual failed. Expected:<0>. Actual:<1>. Unexpected Trajectory Name
But it's better than nothing.
I believe solution is to us L prefix before string
Assert::AreEqual<bool>(true, dict->IsBeginWith("", ""), L"Empty");
You can also try case it like this, which give you wrong results, but lead to right direction of understanding of issue
Assert::AreEqual<bool>(true, dict->IsBeginWith("", ""), (wchar_t*)"Empty"); //Empty
Assert::AreEqual(true, dict->IsBeginWith("A", "A"), (wchar_t*)"Empty2");
Assert::AreEqual(true, dict->IsBeginWith("A", "a"), (wchar_t*)""); //CAPITAL LETTER Check

the reasons of errors in ML code

The below code is just a kind of prototype. What I want to know is why it fails to compile.
fun test(list) =
let
fun inner(list) =
let
val from = #1(hd(list))
in
if null(tl(list)) = false then innerinner(tl(list),from)
else false
end
fun innerinner(list,from) =
if #2(hd(list)) = from then true
else if null(list) = false then innerinner(tl(list),from)
else false
in
inner(list)
end;
The error messages are:
test.txt:7.34-7.44 Error: unbound variable or constructor: innerinner
test.txt:3.2-9.6 Error: unresolved flex record
(can't tell what fields there are besides #1)
test.txt:10.2-13.13 Error: unresolved flex record
(can't tell what fields there are besides #2)
uncaught exception Error
raised at: ../compiler/Toplevel/interact/evalloop.sml:66.19-66.27
....
I am a kind beginner of ML programming. Could anyone teach me what is wrong?
You have quite a few things going on here. If we first look at the errors you are getting.
unbound variable or constructor: innerinner
In sml you can't "use" stuff before it has been declared. It is easily fixed in your case by
swapping the function declarations around, and thus declaring innerinner before inner.
If you ever end up in a case where you want to for example declare two mutually recursive
functions, then this is not an option. In this case you would have to use the keyword and.
unresolved flex record
This is a bit more complicated. It is a type error and has something to do with the fact
that tuples are represented as records internally (I would recommend you go read about
it). Thus when you don't supply enough information, the type system will complain.
I think this QA explains it quite good. In summary, you can't have unbounded tuples
and thus you need to make it clear to the type system how many elements it contains. This
could be done by explicitly type annotating the function declaration.
However in general you ought to use pattern matching instead, as often as feasible.
In general you should always use pattern matching rather than tuple selectors (#1, #2, ...), or list selectors (hd or tl). You just saw why tuple selectors can be "bad" but using list selectors without testing whether or not the list is empty first will give you runtime errors (exceptions).
Putting in such test cases in your code will "blow it up" and make it messy to read. But if you use pattern matching instead you will have some nice clear cut cases in your function definition.
Also often you will tend to writer less code (in my opinion).
Btw, you don't need to put parentheses around single arguments to functions, such as you main definition of the test function.
All in all your function could look something like this:
fun test list =
let
fun innerinner ((x1, x2)::xs,from) =
if x1 = from then true
else innerinner(xs,from)
| innerinner ([], from) = false
fun inner ((x1, x2)::xs) = innerinner(xs,x1)
| inner [] = false
in
inner(list)
end