How do you test for a specific Rust error? - unit-testing

I can find ways to detect if Rust gives me an error,
assert!(fs::metadata(path).is_err())
source
How do I test for a specific error?

You can directly compare the returned Err variant if it impl Debug + PartialEq:
#[derive(Debug, PartialEq)]
enum MyError {
TooBig,
TooSmall,
}
pub fn encode(&self, decoded: &'a Bytes) -> Result<&'a Bytes, MyError> {
if decoded.len() > self.length() as usize {
Err(MyError::TooBig)
} else {
Ok(&decoded)
}
}
assert_eq!(fixed.encode(&[1]), Err(MyError::TooBig));

Following solution doesn't require PartialEq trait to be implemented. For instance std::io::Error does not implement this, and more general solution is required.
In these cases, you can borrow a macro assert_matches from matches crate. It works by giving more succinct way to pattern match, the macro is so short you can just type it too:
macro_rules! assert_err {
($expression:expr, $($pattern:tt)+) => {
match $expression {
$($pattern)+ => (),
ref e => panic!("expected `{}` but got `{:?}`", stringify!($($pattern)+), e),
}
}
}
// Example usages:
assert_err!(your_func(), Err(Error::UrlParsingFailed(_)));
assert_err!(your_func(), Err(Error::CanonicalizationFailed(_)));
assert_err!(your_func(), Err(Error::FileOpenFailed(er)) if er.kind() == ErrorKind::NotFound);
Full playground buildable example, with example Error enum:
#[derive(Debug)]
pub enum Error {
UrlCreationFailed,
CanonicalizationFailed(std::io::Error),
FileOpenFailed(std::io::Error),
UrlParsingFailed(url::ParseError),
}
pub fn your_func() -> Result<(), Error> {
Ok(())
}
#[cfg(test)]
mod test {
use std::io::ErrorKind;
use super::{your_func, Error};
macro_rules! assert_err {
($expression:expr, $($pattern:tt)+) => {
match $expression {
$($pattern)+ => (),
ref e => panic!("expected `{}` but got `{:?}`", stringify!($($pattern)+), e),
}
}
}
#[test]
fn test_failures() {
// Few examples are here:
assert_err!(your_func(), Err(Error::UrlParsingFailed(_)));
assert_err!(your_func(), Err(Error::CanonicalizationFailed(_)));
assert_err!(your_func(), Err(Error::FileOpenFailed(er)) if er.kind() == ErrorKind::NotFound);
}
}

Related

Pass list of one of two structures to the function

New in Go, couldn't find any intuitive way of doing that.
I have such piece of code
tx = getTx()
for _, record := range tx.a {
// do a lot with record.Important
}
for _, record := range tx.b {
// do a lot with record.Important
}
for _, record := range tx.c {
// do a lot with record.Important
}
And the following structs:
type Record1 struct {
// fields of Record1
Important string
}
type Record2 struct {
// fields of Record1
Important string
}
type TX struct {
a []Record1
b []Record1
c []Record2
}
Now the logical is to extract every for logic into the function:
func helper(records) { // Here is the problem
// do a lot with record.Important
}
Problem:
records is a []Record1 or []Record2 type. But it looks like Union types doesn't exists in Golang. So I thought I could pass []string into the helper, but cannot even find an elegant way to get something equivalent to map(lambda r: r.Important, tx.a). There is no high order map function, no list comprehesion. I am not convinced to use raw for loop to solve that.
One approach to do the loop across multiple types is to use interfaces together with generics. Have each Record type implement a getter method for the important field. Then declare an interface that includes that getter method in its method set. Then you can make your helper generic by declaring the interface as its type parameter.
func (r Record1) GetImportant() string { return r.Important }
func (r Record2) GetImportant() string { return r.Important }
type ImportantGetter interface {
GetImporant() string
}
func helper[T ImportantGetter](s []T) {
for _, v := range s {
_ = v.GetImportant()
}
}
Unless I'm misunderstanding your question, it seems like you want to extract all the values in column X from a set of records and then pass those values in as a slice to some function - I'm basing my assumption on your wish that go had something like map().
If what you're after is type-agnosticism, you could certainly use an interface approach like that suggested by mkopriva, but you aren't going to get out of using a for loop - iteration over list types is core to idiomatic go. If you need a mapping function, you're going to have to write one that performs the mapping you want.
I'd note that you do not need generics to do what mkopriva suggests, you can just use an interface without muddying the waters with generics go playground:
package main
import "fmt"
type Record1 struct {
Important string
}
type Record2 struct {
Important string
}
func (r Record1) GetImportant() string { return r.Important }
func (r Record2) GetImportant() string { return r.Important }
type ImportantGetter interface {
GetImportant() string
}
func helper(s []ImportantGetter) {
for _, v := range s {
fmt.Println(v.GetImportant())
}
}
func main() {
records := []ImportantGetter{Record1{Important: "foo"}, Record2{Important: "bar"}}
helper(records)
}
Another approach to the type-agnosticism, and one that's a bit more (IMHO) idiomatic for "I expect all of these types to have a common property," is to use struct embedding and type assertions to build your own Map() function up go playground:
type CommonFields struct {
Important string
}
type Record1 struct {
CommonFields
FieldSpecificToRecord1 string
}
type Record2 struct {
CommonFields
FieldSpecificToRecord2 int
}
func main() {
r1 := Record1{
CommonFields{Important: "I'm r1!"},
"foo",
}
r2 := Record2{
CommonFields{Important: "I'm r2!"},
5,
}
records := []interface{}{r1, r2, "this is not a valid record type"}
fmt.Println(Map(records))
}
func Map(source []interface{}) []string {
destination := make([]string, len(source))
for i, sourceRecord := range source {
if rr, ok := sourceRecord.(Record1); ok {
destination[i] = rr.Important
} else if rr, ok := sourceRecord.(Record2); ok {
destination[i] = rr.Important
} else {
destination[i] = "undefined"
}
}
return destination
}
You'd likely want to make your implementation of Map() accept an argument specifying the field to extract to conform to what you have in other languages, or possibly even just pass in a helper function which does most of the type-specific value extraction.

Unit testing a service that accepts an Fn closure as a callback

I have the following service that registers callbacks to execute at a certain epoch, identified by an i64. The service has a vector of callbacks (that are bounded by the Send + Fn() -> () traits). Each callback can be executed multiple times (hence Fn instead of FnOnce or FnMut). The Send trait is needed because the callbacks will be registered by other threads, and this service will run in the background.
So far so good, but I'd like to test that the callbacks are executed the way they should be (i.e. the i64 epoch ticking in some direction which may (or may not) cause the callback to be executed). The problem is that I cannot seem to be able to think of a way to achieve this. I'm coming from Golang in which it is quite easy to inject a mock callback and assert whether it was called since such limitations are not imposed by the compiler, however when I employ the same methods in Rust, I end up with an FnMut instead of an Fn.
use std::sync::{Arc, Mutex};
use std::collections::HashMap;
struct Service<T: Send + Fn() -> ()> {
triggers: Arc<Mutex<HashMap<i64, Vec<Box<T>>>>>,
}
impl<T: Send + Fn() -> ()> Service<T> {
pub fn build() -> Self {
Service {
triggers: Arc::new(Mutex::new(HashMap::new())),
}
}
pub fn poll(&'static self) {
let hs = Arc::clone(&self.triggers);
tokio::spawn(async move {
loop {
// do some stuff and get `val`
if let Some(v) = hs.lock().unwrap().get(&val) {
for cb in v.iter() {
cb();
}
}
}
});
()
}
pub fn register_callback(&self, val: i64, cb: Box<T>) -> () {
self.triggers
.lock()
.unwrap()
.entry(val)
.or_insert(Vec::new())
.push(cb);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_poll() {
let c = Service::build();
let mut called = false;
let cb = || called = true;
let h: i64 = 10;
c.register_callback(h, Box::new(cb));
assert_eq!(called, false);
}
}
Any ideas on how would this sort of behavior could be tested in Rust? The only thing I can think of is perhaps some channel that would pass a local value to the test and relinquish ownership over it?
The best way would probably be to make your interface as general as possible:
// type bounds on structs are generally unnecessary so I removed it here.
struct Service<T> {
triggers: Arc<Mutex<HashMap<i64, Vec<Box<T>>>>>,
}
impl<T: Send + FnMut() -> ()> Service<T> {
pub fn build() -> Self {
Service {
triggers: Arc::new(Mutex::new(HashMap::new())),
}
}
pub fn poll(&'static self, val: i64) {
let hs = Arc::clone(&self.triggers);
tokio::spawn(async move {
loop {
// do some stuff and get `val`
if let Some(v) = hs.lock().unwrap().get_mut(&val) {
for cb in v.iter_mut() {
cb();
}
}
}
});
()
}
pub fn register_callback(&self, val: i64, cb: Box<T>) -> () {
self.triggers
.lock()
.unwrap()
.entry(val)
.or_insert(Vec::new())
.push(cb);
}
}
But if you can't generalize the interface you can just use an AtomicBool like this:
#[cfg(test)]
mod tests {
use super::*;
use std::sync::atomic::{Ordering, AtomicBool};
#[test]
fn test_poll() {
let c = Service::build();
let mut called = AtomicBool::new(false);
let cb = || called.store(true, Ordering::Relaxed);
let h: i64 = 10;
c.register_callback(h, Box::new(cb));
assert!(!called.load(Ordering::Relaxed));
}
}

Mocking functions in rust

Is there a way to mock regular functions in rust?
Consider the following code:
fn main() {
println!("{}", foo());
}
fn get_user_input() -> u8 {
// Placeholder for some unknown value
42
}
fn foo() -> u8 {
get_user_input()
}
#[cfg(test)]
mod tests {
#[test]
fn test_foo() {
use super::*;
get_user_input = || 12u8;
assert_eq!(foo(), 12u8);
}
}
I would like to unit test foo() without having to rely on the output of get_user_input().
I obviously cannot overwrite get_user_input() like I tried in the example code.
I have only found ways to mock structs, traits and modules but nothing about mocking regular free functions. Am I missing something?
Edit: I have looked primarily at the mockall crate.
You could use cfg:
#[cfg(not(test))]
fn get_user_input() -> u8 {
// Placeholder for some unknown value
42
}
#[cfg(test)]
fn get_user_input() -> u8 {
12
}
playground
Or dependency injection:
pub fn main() {
println!("{}", foo(get_user_input));
}
fn get_user_input() -> u8 {
// Placeholder for some unknown value
42
}
fn foo(get_user_input_: impl Fn() -> u8) -> u8 {
get_user_input_()
}
#[cfg(test)]
mod tests {
#[test]
fn test_foo() {
use super::*;
let get_user_input = || 12u8;
assert_eq!(foo(get_user_input), 12u8);
}
}
playgound

Problem calling C++ Class method from Rust

The following code is generated by bindgen.
extern "C" {
#[doc = "MoraComm Properties"]
#[link_name = "\u{1}_ZN22MoraCommManagerWrapped10propertiesEj"]
pub fn MoraCommManagerWrapped_properties(
this: *mut MoraCommManagerWrapped,
deviceNumber: ::std::os::raw::c_uint,
) -> MoraCommPropertiesWrapped;
}
impl MoraCommManagerWrapped {
#[inline]
pub unsafe fn properties(
&mut self,
deviceNumber: ::std::os::raw::c_uint,
) -> MoraCommPropertiesWrapped {
MoraCommManagerWrapped_properties(self, deviceNumber)
}
#[inline]
pub unsafe fn new() -> Self {
let mut __bindgen_tmp = ::std::mem::MaybeUninit::uninit();
MoraCommManagerWrapped_MoraCommManagerWrapped(__bindgen_tmp.as_mut_ptr());
__bindgen_tmp.assume_init()
}
}
fn main() {
unsafe {
let mut mgr: MoraCommManagerWrapped = MoraCommManagerWrapped::new();
let p = mgr.properties(0); // <- segfault
}
}
When main is run, there is a segfault.
I added logging on the C++ side, that shows that the value of the this pointer is equal to value of the deviceNumber arg.
I call another class that is part of the same bindgen generated bindings and it works.
Any thoughts on what might be going on?

How do I perform a replacement using a formatted string from a regex capture group?

I am doing multiple replacements at once using the regex crate:
extern crate regex;
use regex::{Captures, Regex};
fn transform(string: &str) {
let rgx = Regex::new(r"(\n)|(/\w+)").unwrap();
let res = rgx.replace_all(string, |caps: &Captures| {
if caps.get(1).is_some() {
return " ";
}
match caps.get(2).map(|m: regex::Match| m.as_str()) {
Some(z) => return "nope", // how to return formatted z instead?
None => (),
}
unreachable!();
});
println!("{}", res);
}
fn main() {
transform("no errors");
transform("big\nbad\n/string");
}
Output as expected:
no errors
big bad nope
Instead of "nope", I would like to return z formatted in some way instead. format! doesn't seem like it can be used here due to String / lifetime issues:
match caps.get(2).map(|m: regex::Match| m.as_str()) {
Some(z) => return format!("cmd: {}", z),
None => (),
}
error[E0308]: mismatched types
--> src/main.rs:12:31
|
12 | Some(z) => return format!("cmd: {}", z),
| ^^^^^^^^^^^^^^^^^^^^^ expected &str, found struct `std::string::String`
|
= note: expected type `&str`
found type `std::string::String`
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
What should be done instead?
Note in the error message:
expected &str
It expects a &str because that's the first type returned by your closure:
return " ";
A closure / function can have only one return type, not two.
The simplest fix is to return a String in both cases:
let res = rgx.replace_all(string, |caps: &Captures| {
if caps.get(1).is_some() {
return String::from(" ");
}
let m = caps.get(2).unwrap();
format!("cmd: {}", m.as_str())
});
To be slightly more efficient, you can avoid the String allocation for the space character:
use std::borrow::Cow;
let res = rgx.replace_all(string, |caps: &Captures| {
if caps.get(1).is_some() {
return Cow::from(" ");
}
let m = caps.get(2).unwrap();
Cow::from(format!("cmd: {}", m.as_str()))
});
playground
I've also replaced the match with the => () arm paired with the unreachable! with the shorter unwrap.
See also:
Cannot use `replace_all` from the regex crate: expected (), found String
Using str and String interchangably
Return local String as a slice (&str)