261 lines
8.3 KiB
Python
261 lines
8.3 KiB
Python
# file: tests/test_rust_audits.py
|
|
# version: 2
|
|
|
|
"""Targeted contracts for the optional monotask Rust structure audits."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import pathlib
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
|
|
sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[1] / "scripts"))
|
|
|
|
from audit_rust_blank_lines import audit_file as audit_blanks # noqa: E402
|
|
from audit_rust_const_blocks import audit_file as audit_consts # noqa: E402
|
|
from audit_rust_item_spacing import audit_file as audit_item_spacing # noqa: E402
|
|
from audit_rust_module_order import audit_file as audit_module_order # noqa: E402
|
|
from audit_rust_reexport_blocks import audit_file as audit_reexports # noqa: E402
|
|
from audit_rust_trait_import_blocks import audit_file as audit_trait_imports # noqa: E402
|
|
from _rust_audit_common import selected_files # noqa: E402
|
|
|
|
|
|
class RustAuditTests(unittest.TestCase):
|
|
def setUp(self) -> None:
|
|
self.temporary = tempfile.TemporaryDirectory()
|
|
self.addCleanup(self.temporary.cleanup)
|
|
self.root = pathlib.Path(self.temporary.name)
|
|
|
|
def source(self, text: str) -> pathlib.Path:
|
|
path = self.root / "source.rs"
|
|
path.write_text(text, encoding="utf-8")
|
|
return path
|
|
|
|
def test_clean_body_and_const_visibility_groups(self) -> None:
|
|
path = self.source("""pub const ALPHA: u8 = 1;
|
|
pub const BETA: u8 = 2;
|
|
|
|
pub(crate) const GAMMA: u8 = 3;
|
|
|
|
const OMEGA: u8 = 4;
|
|
|
|
fn sample() {
|
|
let value = r#"{\n\n}"#;
|
|
println!("{value}");
|
|
}
|
|
""")
|
|
self.assertEqual(audit_blanks(path), [])
|
|
self.assertEqual(audit_consts(path), [])
|
|
|
|
def test_blank_body_and_repeated_blank_lines(self) -> None:
|
|
path = self.source("""fn sample() {
|
|
let value = 1;
|
|
|
|
|
|
println!("{value}");
|
|
}
|
|
""")
|
|
issues = audit_blanks(path)
|
|
self.assertTrue(any("consecutive" in issue for issue in issues))
|
|
self.assertTrue(any("inside fn body" in issue for issue in issues))
|
|
|
|
def test_blank_string_and_nested_comment_braces_are_ignored(self) -> None:
|
|
path = self.source('''fn sample() {
|
|
let data = r#"{
|
|
|
|
|
|
}"#;
|
|
/* outer { /* inner } */ outer } */
|
|
println!("{data}");
|
|
}
|
|
''')
|
|
self.assertEqual(audit_blanks(path), [])
|
|
|
|
def test_struct_body_and_tuple_struct(self) -> None:
|
|
path = self.source("""struct Payload {
|
|
x: u8,
|
|
|
|
y: u8,
|
|
}
|
|
|
|
struct Pair(u8, u8);
|
|
""")
|
|
self.assertEqual(len(audit_blanks(path)), 1)
|
|
self.assertIn("struct body", audit_blanks(path)[0])
|
|
|
|
def test_const_order_and_multiline_initializer(self) -> None:
|
|
path = self.source("""const BETA: [u8; 2] = [
|
|
1,
|
|
2,
|
|
];
|
|
const ALPHA: u8 = 1;
|
|
|
|
pub const GAMMA: u8 = 3;
|
|
""")
|
|
issues = audit_consts(path)
|
|
self.assertTrue(any("alphabetical" in issue for issue in issues))
|
|
self.assertTrue(any("visibility" in issue for issue in issues))
|
|
|
|
def test_const_spacing(self) -> None:
|
|
path = self.source("""pub const ALPHA: u8 = 1;
|
|
|
|
pub const BETA: u8 = 2;
|
|
pub(crate) const GAMMA: u8 = 3;
|
|
""")
|
|
issues = audit_consts(path)
|
|
self.assertEqual(sum("blank line(s)" in issue for issue in issues), 2)
|
|
|
|
def test_const_function_does_not_join_const_block(self) -> None:
|
|
path = self.source("""const ZETA: u8 = 1;
|
|
|
|
const fn compute() -> u8 {
|
|
1
|
|
}
|
|
|
|
const ALPHA: u8 = 2;
|
|
""")
|
|
self.assertEqual(audit_consts(path), [])
|
|
|
|
def test_scope_and_missing_path(self) -> None:
|
|
self.source("fn sample() {}\n")
|
|
selected, errors = selected_files([str(self.root), str(self.root / "absent.rs")])
|
|
self.assertEqual(len(selected), 1)
|
|
self.assertEqual(len(errors), 1)
|
|
|
|
def test_cli_status_for_success_violation_and_invalid_selection(self) -> None:
|
|
path = self.source("const ALPHA: u8 = 1;\nconst BETA: u8 = 2;\n")
|
|
script = pathlib.Path(__file__).resolve().parents[1] / "scripts" / "audit_rust_const_blocks.py"
|
|
|
|
def run(subject: pathlib.Path) -> subprocess.CompletedProcess[str]:
|
|
return subprocess.run([sys.executable, "-B", str(script), str(subject)], capture_output=True, text=True, check=False)
|
|
|
|
self.assertEqual(run(path).returncode, 0)
|
|
path.write_text("const BETA: u8 = 1;\nconst ALPHA: u8 = 2;\n", encoding="utf-8")
|
|
self.assertEqual(run(path).returncode, 1)
|
|
self.assertEqual(run(self.root / "missing.rs").returncode, 2)
|
|
|
|
def test_item_spacing_between_distinct_items(self) -> None:
|
|
path = self.source("""struct Alpha {}
|
|
struct Beta {}
|
|
|
|
fn sample() {}
|
|
""")
|
|
issues = audit_item_spacing(path)
|
|
self.assertTrue(any("exactly one blank line" in issue for issue in issues))
|
|
|
|
def test_item_spacing_delegates_compact_declaration_blocks(self) -> None:
|
|
path = self.source("""const ALPHA: u8 = 1;
|
|
|
|
const BETA: u8 = 2;
|
|
|
|
static CACHE: u8 = 0;
|
|
static READY: u8 = 1;
|
|
|
|
type First = u8;
|
|
type Second = u16;
|
|
""")
|
|
self.assertEqual(audit_item_spacing(path), [])
|
|
|
|
def test_item_spacing_inside_impl(self) -> None:
|
|
path = self.source("""struct Sample {}
|
|
|
|
impl Sample {
|
|
fn alpha() {}
|
|
fn beta() {}
|
|
}
|
|
""")
|
|
self.assertTrue(any("exactly one blank line" in issue for issue in audit_item_spacing(path)))
|
|
|
|
def test_item_spacing_does_not_infer_across_macro(self) -> None:
|
|
path = self.source("""struct Alpha {}
|
|
macro_rules! generated { () => {}; }
|
|
struct Beta {}
|
|
""")
|
|
self.assertEqual(audit_item_spacing(path), [])
|
|
|
|
def test_module_order_production_and_tests(self) -> None:
|
|
path = self.source("""mod beta;
|
|
mod alpha;
|
|
#[cfg(test)]
|
|
mod tests;
|
|
""")
|
|
issues = audit_module_order(path)
|
|
self.assertTrue(any("production" in issue for issue in issues))
|
|
path.write_text("#[cfg(test)]\nmod tests;\nmod alpha;\n", encoding="utf-8")
|
|
self.assertTrue(any("test modules must follow" in issue for issue in audit_module_order(path)))
|
|
|
|
def test_module_order_clean_with_path_attribute(self) -> None:
|
|
path = self.source("""mod alpha;
|
|
mod beta;
|
|
|
|
#[cfg(test)]
|
|
#[path = "unit_tests/mod.rs"]
|
|
mod unit_tests;
|
|
""")
|
|
self.assertEqual(audit_module_order(path), [])
|
|
|
|
def test_reexport_blocks_visibility_and_spacing(self) -> None:
|
|
path = self.source("""pub(crate) use self::internal::Internal;
|
|
|
|
pub use self::public::Public;
|
|
""")
|
|
issues = audit_reexports(path)
|
|
self.assertTrue(any("must precede" in issue for issue in issues))
|
|
path.write_text("pub use self::public::Alpha;\n\npub use self::public::Beta;\n", encoding="utf-8")
|
|
self.assertTrue(any("contains an empty line" in issue for issue in audit_reexports(path)))
|
|
|
|
def test_reexport_blocks_clean_with_rustdocs(self) -> None:
|
|
path = self.source("""/// Alpha.
|
|
pub use self::public::Alpha;
|
|
/// Beta.
|
|
pub use self::public::Beta;
|
|
|
|
/// Internal.
|
|
pub(crate) use self::internal::Internal;
|
|
""")
|
|
self.assertEqual(audit_reexports(path), [])
|
|
|
|
def test_trait_import_block_layout(self) -> None:
|
|
path = self.source("""use alpha::AlphaTrait;
|
|
|
|
use beta::BetaTrait;
|
|
|
|
struct Sample {}
|
|
""")
|
|
self.assertTrue(any("contains an empty line" in issue for issue in audit_trait_imports(path)))
|
|
path.write_text("struct Sample {}\n\nuse alpha::AlphaTrait;\n", encoding="utf-8")
|
|
self.assertTrue(any("must precede" in issue for issue in audit_trait_imports(path)))
|
|
|
|
def test_trait_import_block_clean_and_exports_ignored(self) -> None:
|
|
path = self.source("""mod alpha;
|
|
|
|
use alpha::AlphaTrait;
|
|
use beta::BetaTrait;
|
|
|
|
struct Sample {}
|
|
|
|
pub use self::alpha::Alpha;
|
|
""")
|
|
self.assertEqual(audit_trait_imports(path), [])
|
|
|
|
def test_new_audit_cli_statuses(self) -> None:
|
|
path = self.source("mod alpha;\nmod beta;\n")
|
|
root = pathlib.Path(__file__).resolve().parents[1] / "scripts"
|
|
scripts = (
|
|
root / "audit_rust_item_spacing.py",
|
|
root / "audit_rust_module_order.py",
|
|
root / "audit_rust_reexport_blocks.py",
|
|
root / "audit_rust_trait_import_blocks.py",
|
|
)
|
|
for script in scripts:
|
|
result = subprocess.run([sys.executable, "-B", str(script), str(path)], capture_output=True, text=True, check=False)
|
|
self.assertEqual(result.returncode, 0, (script, result.stderr))
|
|
missing = subprocess.run([sys.executable, "-B", str(script), str(self.root / "missing.rs")], capture_output=True, text=True, check=False)
|
|
self.assertEqual(missing.returncode, 2, (script, missing.stderr))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|