微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

我的 Rust 单元测试中出现“来自索引 1 的 1 个字节的无效 utf-8 序列”和“munmup_chunk():无效指针”错误 下一个问题:为什么你的第三次测试失败了:

如何解决我的 Rust 单元测试中出现“来自索引 1 的 1 个字节的无效 utf-8 序列”和“munmup_chunk():无效指针”错误 下一个问题:为什么你的第三次测试失败了:

只有 lib.rs 文件

use libc::{self,O_RDONLY,strerror,open,close,__errno_location,read};
use std::ffi::{CString,CStr};
use std::{fmt,process};

// File descriptor
struct Fd {
    fd: i32,}

impl Fd {
    fn new(fd: i32) -> Fd {
        Fd { fd }
    }

    fn get(&self) -> i32 {
        self.fd
    }
}

impl Drop for Fd {
    fn drop(&mut self) {
        unsafe {
            println!("closing fd - {}",self.fd);
            match close(self.fd) {
                -1 => Err( LacError::new().unwrap_or_else(|e| {
                        eprintln!("{}",e);
                        process::exit(1); 
                    }) ).unwrap_or_else(|e| {
                        eprintln!("{}",e);
                        process::exit(1);}),_ => (),}
        }
    }
}

// Linux API call Error
#[derive(Debug)]
struct LacError(String);

impl LacError {
    fn new() -> Result<LacError,Box<dyn std::error::Error>> {
        unsafe {
            Ok( LacError( CString::from_raw(strerror(*__errno_location()))
                .into_string()?) )
        }
    }
}

impl fmt::display for LacError {
    fn fmt(&self,f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,"{}",self.0)
    }
}

impl std::error::Error for LacError {}

// lac (Linux API call) functions
fn lac_open(path: &str) -> Result<Fd,Box<dyn std::error::Error>> {
    unsafe {
        let path_holder = CString::new(path)?;
        let path_ptr = path_holder.as_ptr();

        match open(path_ptr,O_RDONLY) {
            -1 => Err(Box::new(LacError::new()?)),fd => Ok(Fd::new(fd)) 
        }
    }
}

fn lac_read(fd: &Fd,buf: &mut String,count: usize) -> Result<isize,Box<dyn std::error::Error>>  {
    let buf_holder = CString::new("")?;
    let buf_ptr = buf_holder.as_ptr();

    unsafe {
        match read(fd.get(),buf_ptr as *mut libc::c_void,count) {
            0 => Ok(0),-1 => Err(Box::new(LacError::new()?)),num_of_red_bytes => {
                buf.push_str(CStr::from_ptr(buf_ptr).to_str()?);
                Ok(num_of_red_bytes)
            },}
    }
}    
 
// TESTS
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn whether_correctly_open() {
        let path = "test_file.txt";
        assert_ne!(match lac_open(path) {Ok(fd) => fd.get(),Err(_) => -1},-1);
    }

    #[test]
    #[should_panic]
    fn whether_correctly_panic() {
        let path = "testfile.txt";// For first issue "testfile.txt",for second "test_file.txt"
        match lac_open(path) {
            Ok(_) => (),Err(e) => panic!("{}",e),}
    }

    #[test]
    fn whether_correctly_read() {
        let path = "test_file.txt"; 
        let mut buf = String::from("");

        let fd = lac_open(path)
            .unwrap_or_else(|e| {panic!("{}",e);});
        let count = lac_read(&fd,&mut buf,1)
            .unwrap_or_else(|e| {panic!("{}",e);});
        println!("{} {}",buf,count);
        assert_eq!(buf,"s"); 
    }
}

起初,当我运行“货物测试 -- --show-output”时,第一个测试成功通过,但第二个(第三个测试省略了一段时间)不仅失败了(

running 3 tests
test tests::whether_correctly_open ... ok
munmap_chunk(): invalid pointer
error: test Failed,to rerun pass '--lib'

Caused by:
process didn't exit successfully: `/home/Michail/rPrgs/usl/target/debug/deps/usl-1be62c27ff5543fb --show-output` (signal: 6,SIGABRT: process abort signal)

) 操作系统向该进程发送信号可能是因为 LacError::new() 方法中的操作:

impl LacError {
    fn new() -> Result<LacError,Box<dyn std::error::Error>> {
        unsafe {
            Ok( LacError( CString::from_raw(strerror(*__errno_location()))
                .into_string()?) )
        }
    }
}

而且我完全不知道我哪里出错了。

第二次,当我用“test_file.txt”交换“testfile.txt”并使第二次测试真的失败时,我运行'cargo test -- --show-output'和(我相信`cause 'cargo test' run认情况下在几个线程中)接收

running 3 tests
test tests::whether_correctly_open ... ok
test tests::whether_correctly_panic ... Failed
test tests::whether_correctly_read ... ok

successes:

---- tests::whether_correctly_open stdout ----
closing fd - 3

---- tests::whether_correctly_read stdout ----
s 1
closing fd - 3


successes:
    tests::whether_correctly_open
    tests::whether_correctly_read

failures:

---- tests::whether_correctly_panic stdout ----
closing fd - 3
note: test did not panic as expected

failures:
    tests::whether_correctly_panic

test result: Failed. 2 passed; 1 Failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

error: test Failed,to rerun pass '--lib'

如果运行几次

test tests::whether_correctly_open ... ok
test tests::whether_correctly_panic ... Failed
test tests::whether_correctly_read ... Failed

successes:

---- tests::whether_correctly_open stdout ----
closing fd - 3


successes:
    tests::whether_correctly_open

failures:

---- tests::whether_correctly_panic stdout ----
closing fd - 3
note: test did not panic as expected
---- tests::whether_correctly_read stdout ----
thread 'tests::whether_correctly_read' panicked at 'invalid utf-8 sequence of 1 bytes from index 2',src/lib.rs:119:34
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
closing fd - 3


failures:
    tests::whether_correctly_panic
    tests::whether_correctly_read

test result: Failed. 1 passed; 2 Failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

error: test Failed,to rerun pass '--lib'

也就是说,随着时间的推移,当我从上面重复运行命令时,我会收到不同的结果。我认为 utf-8 的问题与该代码有关

fn lac_read(fd: &Fd,num_of_red_bytes => {
                buf.push_str(CStr::from_ptr(buf_ptr).to_str()?);//<-----------------------
                Ok(num_of_red_bytes)
            },}
    }
}    

此外,当我重复运行 'cargo test -- --show-output --test-threads=1' 时,第三次测试总是失败。

UPD:在测试之前,我通过

在“test_file.txt”中编写了常见的拉丁字符
echo sometext > test_file.txt

最终更新:我决定的第一个问题是从借用的 strerror 字符串中创建新的拥有的字符串:

impl LacError {
    fn new() -> Result<LacError,Box<dyn std::error::Error>> {
        unsafe {
            Ok( LacError( String::from( CStr::from_ptr(strerror(*__errno_location())).to_str()? ) ) )
        }
    }
}

第二个是最有趣的,因为 CString 不是可增长的字符串,读取系统调用会将字符插入到空格中。我决定编写 for 循环来推送临时“容器”中可增长的拥有字符串的“计数”空间数:

fn lac_read(fd: &Fd,Box<dyn std::error::Error>>  {
    let mut container = String::new();
    for _ in 0..count {
        container.push(' ');
    }
    let buf_holder = CString::new(container.as_str())?;
    let buf_ptr = buf_holder.into_raw();

    unsafe {
        match read(fd.get(),num_of_red_bytes => {
                buf.push_str(CString::from_raw(buf_ptr).to_str()?);
                Ok(num_of_red_bytes)
            },}
    }
}    

解决方法

问题在于 CString 会在超出范围时尝试取消分配其字符串。相反,您应该使用 CStr ,它不会这样做:

impl LacError {
    fn new() -> Result<LacError,Box<dyn std::error::Error>> {
        unsafe {
            Ok(LacError(
                CStr::from_ptr(strerror(*__errno_location())).to_string_lossy().to_string()
            ))
        }
    }
}

通常创建/分配/等的人负责销毁/取消分配/等,除非文档另有明确说明。由于字符串是由操作系统分配的,it's an error to try to de-allocate it from the application.

下一个问题:为什么你的第三次测试失败了:

read() 调用将最多 count 个字节读入提供的缓冲区。但它不会调整缓冲区的大小。您的应用程序创建一个大小为 1 的缓冲区(它是 1,因为 C 字符串以 0 结尾):

let buf_holder = CString::new("")?;

问题是 C 字符串必须 0 终止。因此,如果您读取的内容不是 0,CStr::from_ptr() 将尝试从未初始化的缓冲区外的内存中读取,这是未定义的行为。您可以通过更改计数使其 100% 重现:

let count = lac_read(&fd,&mut buf,100).unwrap();

现在您的应用程序将尝试将 100 个字节读入该 1 个字节的缓冲区,并破坏之后的所有内容。有了这个,我总是得到:

malloc(): corrupted top size

所以为了解决这个问题,你必须确保你的缓冲区足够大以容纳数据(不要忘记尾随的 0!)

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。