【问题标题】:Why does a rustdoc test with unsafe code fail but the same code in an integration test passes?为什么使用不安全代码的 rustdoc 测试失败但集成测试中的相同代码通过?
【发布时间】:2018-07-31 03:27:38
【问题描述】:

我正在尝试编写一个名为 insert 的方法的 rustdoc 测试。 在测试的最后一行调用了测试函数,当我将其注释掉时,测试就通过了。

错误信息:

$ cargo test
   Compiling reproduce v0.1.0 
(file:///home/user/reproduce)                                                           
    Finished dev [unoptimized + debuginfo] target(s) in 2.03s
     Running target/debug/deps/reproduce-17ad4bb50aa9c47e

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

   Doc-tests reproduce

running 1 test
test src/lib.rs - MyStruct::method (line 19) ... FAILED

failures:

failures:
    src/lib.rs - MyStruct::method (line 19)

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out

error: test failed, to rerun pass '--doc'

代码:

impl MyStruct {
    pub fn new(lsb: u8, step: usize) -> MyStruct {
        let mask: u8 = match lsb {
            1 => 0b0000_0001,
            2 => 0b0000_0011,
            _ => 0b0000_1111
        };

        MyStruct { lsb, mask, step }
    }

    /// # Examples
    /// ```
    /// extern crate reproduce;
    /// extern crate rgb;
    ///
    /// use std::slice;
    /// use rgb::RGB;
    ///
    /// let msg = "This is a secret message".as_bytes();
    /// let img = vec![RGB {r: 0, g: 0, b: 0}; 800*500];
    ///
    /// // Create a reference to the bytes of img
    /// let p: *const Vec<RGB<u8>> = &img;
    /// let p: *const u8 = p as *const u8;
    /// let p: &[u8] = unsafe { 
    ///     slice::from_raw_parts(p, 3*800*500)
    /// };
    ///
    /// let settings = reproduce::MyStruct::new(2, 12);
    /// let new_data = settings.method(p, &msg);
    /// ```
    pub fn method(&self, img: &[u8], msg: &[u8]) -> Vec<u8> {
        let mut ret = img.to_vec();
        let mut n = 0;

        for ch in msg.iter() {
            for i in 1..=8/self.lsb {
                let shift = (self.lsb*i) as u32;
                ret[n] = (ret[n] & !self.mask) |
                    (ch & self.mask.rotate_right(shift)).rotate_left(shift);
                n += self.step;
            }
        }

        ret
    }
}

难道 rustdoc 测试通过特殊规则(例如使用assert! 宏来证明它返回正确的值)?

我问是因为我的方法通过了非常相似的代码的集成测试,所以我非常有信心它是正确的

【问题讨论】:

  • 请查看如何创建minimal reproducible example,然后查看edit 您的问题以包含它。我们无法分辨代码中存在哪些 crate、类型、特征、字段等。尝试在Rust Playground 上重现您的错误,或者您可以在全新的 Cargo 项目中重现它。还有Rust-specific MCVE tips
  • 与“标准”Rust 测试相比,测试成功没有特殊规则。
  • 创建问题的最小再现。这就是我提供的所有链接所说的。他们并没有说要把你的整个代码都放进去。我几乎保证你可以在一个全新的 Cargo 项目中用不到 25 行代码创建一个重现问题的东西。
  • 好的,我试试,谢谢。
  • 一旦程序中存在未定义的行为,它就可以做任何事情,这意味着它完全有可能在某些情况下崩溃,但在其他情况下不会崩溃,所以它可能只是 rustdoc 的情况是它崩溃的地方。

标签: rust rustdoc


【解决方案1】:

这与它在 rustdoc 测试中运行的事实无关。您的代码表现出未定义的行为,因为它不健全。在测试之外运行相同的代码也会让我感到恐慌。

您正在对内存的布局方式做出假设 - 任何编译器保证都不会支持这些假设。获取指针的“正确”方法是:

let p = img.as_ptr() as *const u8;

然而,虽然这消除了Vec 布局的假设,但其余代码仍然假设RGB 的布局。此更改不再让我感到恐慌,但我无法确定它是否正常工作正确,我不知道它是否会在其他计算机上中断,或者在下一次 Rust 更新后在我的计算机上中断.

我问是因为我的方法通过了非常相似的代码的集成测试,所以我很有信心它是正确的

这正是Undefined Behaviour的危险,这就是为什么unsafe应该只用在不仅经过良好测试而且假设和保证被很好理解的代码中。 p>

【讨论】:

  • 我明白了……我掉进了最明显的陷阱。事实证明,箱子 png 负责一切。经过大量调查,我认为这是唯一可以为您提供像素作为原始字节的 png-decoder crate。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-15
  • 2016-01-05
  • 1970-01-01
  • 2020-05-02
  • 2016-06-06
  • 1970-01-01
相关资源
最近更新 更多