如何解决我的 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 举报,一经查实,本站将立刻删除。