mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-08 12:56:48 +00:00
Add ktest cases
This commit is contained in:
parent
79b0866259
commit
63831dc2df
330
kernel/comps/systree/src/test.rs
Normal file
330
kernel/comps/systree/src/test.rs
Normal file
@ -0,0 +1,330 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use alloc::{
|
||||
borrow::Cow,
|
||||
string::{String, ToString},
|
||||
sync::{Arc, Weak},
|
||||
vec::Vec,
|
||||
};
|
||||
use core::{any::Any, fmt::Debug};
|
||||
|
||||
use ostd::{
|
||||
mm::{FallibleVmRead, FallibleVmWrite, VmReader, VmWriter},
|
||||
prelude::ktest,
|
||||
};
|
||||
|
||||
use super::{
|
||||
Error, Result, SysAttrFlags, SysAttrSet, SysAttrSetBuilder, SysBranchNode, SysBranchNodeFields,
|
||||
SysNode, SysNodeId, SysNodeType, SysObj, SysStr, SysSymlink, SysTree,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct DeviceNode {
|
||||
fields: SysBranchNodeFields<dyn SysObj>,
|
||||
self_ref: Weak<Self>,
|
||||
}
|
||||
|
||||
impl DeviceNode {
|
||||
fn new(name: &str) -> Arc<Self> {
|
||||
let mut builder = SysAttrSetBuilder::new();
|
||||
builder
|
||||
.add(Cow::Borrowed("model"), SysAttrFlags::CAN_READ)
|
||||
.add(Cow::Borrowed("vendor"), SysAttrFlags::CAN_READ)
|
||||
.add(
|
||||
Cow::Borrowed("status"),
|
||||
SysAttrFlags::CAN_READ | SysAttrFlags::CAN_WRITE,
|
||||
);
|
||||
|
||||
let attrs = builder.build().expect("Failed to build attribute set");
|
||||
let name_owned: SysStr = name.to_string().into();
|
||||
let fields = SysBranchNodeFields::new(name_owned, attrs);
|
||||
|
||||
Arc::new_cyclic(|weak_self| DeviceNode {
|
||||
fields,
|
||||
self_ref: weak_self.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl SysObj for DeviceNode {
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn arc_as_node(&self) -> Option<Arc<dyn SysNode>> {
|
||||
self.self_ref
|
||||
.upgrade()
|
||||
.map(|arc_self| arc_self as Arc<dyn SysNode>)
|
||||
}
|
||||
|
||||
fn arc_as_branch(&self) -> Option<Arc<dyn SysBranchNode>> {
|
||||
self.self_ref
|
||||
.upgrade()
|
||||
.map(|arc_self| arc_self as Arc<dyn SysBranchNode>)
|
||||
}
|
||||
|
||||
fn id(&self) -> &SysNodeId {
|
||||
self.fields.id()
|
||||
}
|
||||
|
||||
fn type_(&self) -> SysNodeType {
|
||||
SysNodeType::Branch
|
||||
}
|
||||
|
||||
fn name(&self) -> SysStr {
|
||||
self.fields.name().to_string().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl SysNode for DeviceNode {
|
||||
fn node_attrs(&self) -> &SysAttrSet {
|
||||
self.fields.attr_set()
|
||||
}
|
||||
|
||||
fn read_attr(&self, name: &str, writer: &mut VmWriter) -> Result<usize> {
|
||||
// Check if attribute exists
|
||||
if !self.fields.attr_set().contains(name) {
|
||||
return Err(Error::AttributeError);
|
||||
}
|
||||
|
||||
let attr = self.fields.attr_set().get(name).unwrap();
|
||||
// Check if attribute is readable
|
||||
if !attr.flags().contains(SysAttrFlags::CAN_READ) {
|
||||
return Err(Error::PermissionDenied);
|
||||
}
|
||||
let value = match name {
|
||||
"model" => "MyDevice",
|
||||
"vendor" => "ExampleVendor",
|
||||
"status" => "online",
|
||||
_ => "",
|
||||
};
|
||||
|
||||
// Write the value to the provided writer
|
||||
writer
|
||||
.write_fallible(&mut (value.as_bytes()).into())
|
||||
.map_err(|_| Error::AttributeError)
|
||||
}
|
||||
|
||||
fn write_attr(&self, name: &str, reader: &mut VmReader) -> Result<usize> {
|
||||
// Get attribute and check if it exists
|
||||
let attr = self
|
||||
.fields
|
||||
.attr_set()
|
||||
.get(name)
|
||||
.ok_or(Error::AttributeError)?;
|
||||
|
||||
// Check if attribute is writable
|
||||
if !attr.flags().contains(SysAttrFlags::CAN_WRITE) {
|
||||
return Err(Error::PermissionDenied);
|
||||
}
|
||||
|
||||
// Read new value from the provided reader
|
||||
let mut buffer = [0u8; 256];
|
||||
let mut writer = VmWriter::from(&mut buffer[..]);
|
||||
let read_len = reader
|
||||
.read_fallible(&mut writer)
|
||||
.map_err(|_| Error::AttributeError)?;
|
||||
|
||||
Ok(read_len)
|
||||
}
|
||||
}
|
||||
|
||||
impl SysBranchNode for DeviceNode {
|
||||
fn visit_child_with(&self, name: &str, f: &mut dyn FnMut(Option<&dyn SysNode>)) {
|
||||
let children = self.fields.children.read();
|
||||
children
|
||||
.get(name)
|
||||
.map(|child| {
|
||||
child
|
||||
.arc_as_node()
|
||||
.map(|node| f(Some(node.as_ref())))
|
||||
.unwrap_or_else(|| f(None))
|
||||
})
|
||||
.unwrap_or_else(|| f(None));
|
||||
}
|
||||
|
||||
fn visit_children_with(&self, min_id: u64, f: &mut dyn FnMut(&Arc<dyn SysObj>) -> Option<()>) {
|
||||
let children = self.fields.children.read();
|
||||
for child in children
|
||||
.values()
|
||||
.filter(|child| child.id().as_u64() >= min_id)
|
||||
{
|
||||
if f(child).is_none() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn child(&self, name: &str) -> Option<Arc<dyn SysObj>> {
|
||||
let children = self.fields.children.read();
|
||||
children.get(name).cloned()
|
||||
}
|
||||
|
||||
fn children(&self) -> Vec<Arc<dyn SysObj>> {
|
||||
let children = self.fields.children.read();
|
||||
children.values().cloned().collect()
|
||||
}
|
||||
|
||||
fn count_children(&self) -> usize {
|
||||
self.fields.children.read().len()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct SymlinkNode {
|
||||
id: SysNodeId,
|
||||
name: SysStr,
|
||||
target: String,
|
||||
self_ref: Weak<Self>,
|
||||
}
|
||||
|
||||
impl SymlinkNode {
|
||||
fn new(name: &str, target: &str) -> Arc<Self> {
|
||||
Arc::new_cyclic(|weak_self| SymlinkNode {
|
||||
id: SysNodeId::new(),
|
||||
name: name.to_string().into(),
|
||||
target: target.to_string(),
|
||||
self_ref: weak_self.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl SysObj for SymlinkNode {
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn arc_as_symlink(&self) -> Option<Arc<dyn SysSymlink>> {
|
||||
self.self_ref
|
||||
.upgrade()
|
||||
.map(|arc_self| arc_self as Arc<dyn SysSymlink>)
|
||||
}
|
||||
|
||||
fn id(&self) -> &SysNodeId {
|
||||
&self.id
|
||||
}
|
||||
|
||||
fn type_(&self) -> SysNodeType {
|
||||
SysNodeType::Symlink
|
||||
}
|
||||
|
||||
fn name(&self) -> SysStr {
|
||||
self.name.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl SysSymlink for SymlinkNode {
|
||||
fn target_path(&self) -> &str {
|
||||
&self.target
|
||||
}
|
||||
}
|
||||
|
||||
#[ktest]
|
||||
fn systree_singleton() {
|
||||
// Get the SysTree singleton
|
||||
let sys_tree = SysTree::new();
|
||||
|
||||
// Access the root node
|
||||
let root = sys_tree.root();
|
||||
|
||||
// Check if root node exists
|
||||
assert!(root.is_root());
|
||||
assert_eq!(root.name(), "");
|
||||
assert_eq!(root.type_(), SysNodeType::Leaf);
|
||||
}
|
||||
|
||||
#[ktest]
|
||||
fn node_hierarchy() {
|
||||
// Create device node hierarchy
|
||||
let root_device = DeviceNode::new("root_device");
|
||||
|
||||
// Add child nodes
|
||||
{
|
||||
let child1 = DeviceNode::new("child1");
|
||||
let child2 = DeviceNode::new("child2");
|
||||
root_device
|
||||
.fields
|
||||
.children
|
||||
.write()
|
||||
.insert(Cow::Borrowed("child1"), child1);
|
||||
root_device
|
||||
.fields
|
||||
.children
|
||||
.write()
|
||||
.insert(Cow::Borrowed("child2"), child2);
|
||||
}
|
||||
|
||||
// Verify number of child nodes
|
||||
assert_eq!(root_device.fields.children.read().len(), 2);
|
||||
|
||||
// Get specific child node
|
||||
let child = root_device.child("child1").unwrap();
|
||||
assert_eq!(child.name(), "child1");
|
||||
|
||||
// Traverse all child nodes
|
||||
let all_children: Vec<_> = root_device.children();
|
||||
assert_eq!(all_children.len(), 2);
|
||||
}
|
||||
|
||||
#[ktest]
|
||||
fn attributes() {
|
||||
let device = DeviceNode::new("test_device");
|
||||
|
||||
// Read read-only attribute
|
||||
let model = device.show_attr("model").unwrap();
|
||||
assert_eq!(model, "MyDevice");
|
||||
|
||||
// Read read-write attribute
|
||||
let status = device.show_attr("status").unwrap();
|
||||
assert_eq!(status, "online");
|
||||
|
||||
// Modify read-write attribute
|
||||
let len = device.store_attr("status", "offline").unwrap();
|
||||
assert_eq!(len, 7);
|
||||
|
||||
// Attempt to modify read-only attribute (should fail)
|
||||
let result = device.store_attr("model", "NewModel");
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[ktest]
|
||||
fn symlinks() {
|
||||
let device = DeviceNode::new("device");
|
||||
|
||||
// Create symlink pointing to device
|
||||
let symlink = SymlinkNode::new("device_link", "/sys/devices/device");
|
||||
|
||||
// Verify symlink attributes
|
||||
assert_eq!(symlink.type_(), SysNodeType::Symlink);
|
||||
assert_eq!(symlink.target_path(), "/sys/devices/device");
|
||||
|
||||
// Add symlink to device tree
|
||||
{
|
||||
device
|
||||
.fields
|
||||
.children
|
||||
.write()
|
||||
.insert(Cow::Borrowed("device_link"), symlink.clone());
|
||||
}
|
||||
|
||||
// Verify symlink was added correctly
|
||||
let symlink_obj = device.child("device_link").unwrap();
|
||||
let symlink_node = symlink_obj.arc_as_symlink().unwrap();
|
||||
assert_eq!(symlink_node.target_path(), "/sys/devices/device");
|
||||
}
|
||||
|
||||
#[ktest]
|
||||
fn error_handling() {
|
||||
let device = DeviceNode::new("error_test");
|
||||
|
||||
// Attempt to access non-existent attribute
|
||||
let result = device.show_attr("nonexistent");
|
||||
match result {
|
||||
Err(Error::AttributeError) => (),
|
||||
_ => panic!("Failed to handle non-existent attribute error"),
|
||||
}
|
||||
|
||||
// Attempt to access non-existent child node
|
||||
let child = device.child("nonexistent");
|
||||
assert!(child.is_none());
|
||||
}
|
641
kernel/src/fs/sysfs/test.rs
Normal file
641
kernel/src/fs/sysfs/test.rs
Normal file
@ -0,0 +1,641 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use alloc::{
|
||||
borrow::Cow,
|
||||
collections::BTreeMap,
|
||||
format,
|
||||
string::{String, ToString},
|
||||
sync::{Arc, Weak},
|
||||
vec,
|
||||
vec::Vec,
|
||||
};
|
||||
use core::{any::Any, fmt::Debug};
|
||||
|
||||
use ostd::{
|
||||
mm::{FallibleVmRead, FallibleVmWrite, VmReader, VmWriter},
|
||||
prelude::ktest,
|
||||
sync::RwLock,
|
||||
};
|
||||
use systree::{
|
||||
init_for_ktest, singleton as systree_singleton, Error as SysTreeError, Result as SysTreeResult,
|
||||
SysAttrFlags, SysAttrSet, SysAttrSetBuilder, SysBranchNode, SysBranchNodeFields, SysNode,
|
||||
SysNodeId, SysNodeType, SysNormalNodeFields, SysObj, SysStr, SysSymlink, SysTree,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
fs::{
|
||||
sysfs::fs::SysFs,
|
||||
utils::{DirentVisitor, FileSystem, InodeMode, InodeType},
|
||||
},
|
||||
time::clocks::init_for_ktest as time_init_for_ktest,
|
||||
Result,
|
||||
};
|
||||
|
||||
// --- Mock SysTree Components ---
|
||||
// Sysfs acts as a view layer over the systree component.
|
||||
// These mocks simulate the systree interface (SysNode, SysBranchNode, etc.)
|
||||
|
||||
// Refactor MockLeafNode to use SysNormalNodeFields
|
||||
#[derive(Debug)]
|
||||
struct MockLeafNode {
|
||||
fields: SysNormalNodeFields,
|
||||
data: RwLock<BTreeMap<String, String>>, // Store attribute data
|
||||
self_ref: Weak<Self>,
|
||||
}
|
||||
|
||||
impl MockLeafNode {
|
||||
fn new(name: &str, read_attrs: &[&str], write_attrs: &[&str]) -> Arc<Self> {
|
||||
let name_owned: SysStr = name.to_string().into(); // Convert to owned SysStr
|
||||
|
||||
let mut builder = SysAttrSetBuilder::new();
|
||||
let mut data = BTreeMap::new();
|
||||
for &attr_name in read_attrs {
|
||||
builder.add(Cow::Owned(attr_name.to_string()), SysAttrFlags::CAN_READ);
|
||||
data.insert(attr_name.to_string(), format!("val_{}", attr_name)); // Initial value
|
||||
}
|
||||
for &attr_name in write_attrs {
|
||||
builder.add(
|
||||
Cow::Owned(attr_name.to_string()),
|
||||
SysAttrFlags::CAN_READ | SysAttrFlags::CAN_WRITE,
|
||||
);
|
||||
data.insert(attr_name.to_string(), format!("val_{}", attr_name)); // Initial value
|
||||
}
|
||||
|
||||
let attrs = builder.build().expect("Failed to build attribute set");
|
||||
let fields = SysNormalNodeFields::new(name_owned, attrs);
|
||||
|
||||
Arc::new_cyclic(|weak_self| MockLeafNode {
|
||||
fields,
|
||||
data: RwLock::new(data),
|
||||
self_ref: weak_self.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl SysObj for MockLeafNode {
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
fn arc_as_node(&self) -> Option<Arc<dyn SysNode>> {
|
||||
self.self_ref
|
||||
.upgrade()
|
||||
.map(|arc_self| arc_self as Arc<dyn SysNode>)
|
||||
}
|
||||
fn id(&self) -> &SysNodeId {
|
||||
self.fields.id()
|
||||
}
|
||||
fn type_(&self) -> SysNodeType {
|
||||
SysNodeType::Leaf
|
||||
}
|
||||
fn name(&self) -> SysStr {
|
||||
Cow::Owned(self.fields.name().to_string()) // Convert to Cow::Owned
|
||||
}
|
||||
}
|
||||
|
||||
impl SysNode for MockLeafNode {
|
||||
fn node_attrs(&self) -> &SysAttrSet {
|
||||
self.fields.attr_set()
|
||||
}
|
||||
|
||||
fn read_attr(&self, name: &str, writer: &mut VmWriter) -> SysTreeResult<usize> {
|
||||
let attr = self
|
||||
.fields
|
||||
.attr_set()
|
||||
.get(name)
|
||||
.ok_or(SysTreeError::AttributeError)?;
|
||||
if !attr.flags().contains(SysAttrFlags::CAN_READ) {
|
||||
return Err(SysTreeError::PermissionDenied);
|
||||
}
|
||||
let data = self.data.read();
|
||||
let value = data.get(name).ok_or(SysTreeError::AttributeError)?; // Should exist if in attrs
|
||||
let bytes = value.as_bytes();
|
||||
writer
|
||||
.write_fallible(&mut (&bytes[..]).into())
|
||||
.map_err(|_| SysTreeError::AttributeError)
|
||||
}
|
||||
|
||||
fn write_attr(&self, name: &str, reader: &mut VmReader) -> SysTreeResult<usize> {
|
||||
let attr = self
|
||||
.fields
|
||||
.attr_set()
|
||||
.get(name)
|
||||
.ok_or(SysTreeError::AttributeError)?;
|
||||
if !attr.flags().contains(SysAttrFlags::CAN_WRITE) {
|
||||
return Err(SysTreeError::PermissionDenied);
|
||||
}
|
||||
|
||||
let mut buffer = [0u8; 1024]; // Max write size for test
|
||||
let mut writer = VmWriter::from(&mut buffer[..]);
|
||||
let read_len = reader
|
||||
.read_fallible(&mut writer)
|
||||
.map_err(|_| SysTreeError::AttributeError)?;
|
||||
|
||||
let new_value = String::from_utf8_lossy(&buffer[..read_len]).to_string();
|
||||
|
||||
let mut data = self.data.write();
|
||||
data.insert(name.to_string(), new_value);
|
||||
|
||||
Ok(read_len)
|
||||
}
|
||||
}
|
||||
|
||||
// Refactor MockBranchNode to use SysBranchNodeFields
|
||||
#[derive(Debug)]
|
||||
struct MockBranchNode {
|
||||
fields: SysBranchNodeFields<dyn SysObj>,
|
||||
self_ref: Weak<Self>,
|
||||
}
|
||||
|
||||
impl MockBranchNode {
|
||||
fn new(name: &str) -> Arc<Self> {
|
||||
let name_owned: SysStr = name.to_string().into(); // Convert to owned SysStr
|
||||
|
||||
let mut builder = SysAttrSetBuilder::new();
|
||||
builder.add(Cow::Borrowed("branch_attr"), SysAttrFlags::CAN_READ);
|
||||
let attrs = builder
|
||||
.build()
|
||||
.expect("Failed to build branch attribute set");
|
||||
|
||||
let fields = SysBranchNodeFields::new(name_owned, attrs);
|
||||
|
||||
Arc::new_cyclic(|weak_self| MockBranchNode {
|
||||
fields,
|
||||
self_ref: weak_self.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
fn add_child(&self, child: Arc<dyn SysObj>) {
|
||||
self.fields.add_child(child).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl SysObj for MockBranchNode {
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
fn arc_as_node(&self) -> Option<Arc<dyn SysNode>> {
|
||||
self.self_ref
|
||||
.upgrade()
|
||||
.map(|arc_self| arc_self as Arc<dyn SysNode>)
|
||||
}
|
||||
fn arc_as_branch(&self) -> Option<Arc<dyn SysBranchNode>> {
|
||||
self.self_ref
|
||||
.upgrade()
|
||||
.map(|arc_self| arc_self as Arc<dyn SysBranchNode>)
|
||||
}
|
||||
fn id(&self) -> &SysNodeId {
|
||||
self.fields.id()
|
||||
}
|
||||
fn type_(&self) -> SysNodeType {
|
||||
SysNodeType::Branch
|
||||
}
|
||||
fn name(&self) -> SysStr {
|
||||
Cow::Owned(self.fields.name().to_string()) // Convert to Cow::Owned
|
||||
}
|
||||
}
|
||||
|
||||
impl SysNode for MockBranchNode {
|
||||
fn node_attrs(&self) -> &SysAttrSet {
|
||||
self.fields.attr_set()
|
||||
}
|
||||
|
||||
fn read_attr(&self, name: &str, writer: &mut VmWriter) -> SysTreeResult<usize> {
|
||||
let attr = self
|
||||
.fields
|
||||
.attr_set()
|
||||
.get(name)
|
||||
.ok_or(SysTreeError::AttributeError)?;
|
||||
if !attr.flags().contains(SysAttrFlags::CAN_READ) {
|
||||
return Err(SysTreeError::PermissionDenied);
|
||||
}
|
||||
let value = match name {
|
||||
"branch_attr" => "branch_value",
|
||||
_ => return Err(SysTreeError::AttributeError),
|
||||
};
|
||||
let bytes = value.as_bytes();
|
||||
writer
|
||||
.write_fallible(&mut (&bytes[..]).into())
|
||||
.map_err(|_| SysTreeError::AttributeError)
|
||||
}
|
||||
|
||||
fn write_attr(&self, name: &str, _reader: &mut VmReader) -> SysTreeResult<usize> {
|
||||
let attr = self
|
||||
.fields
|
||||
.attr_set()
|
||||
.get(name)
|
||||
.ok_or(SysTreeError::AttributeError)?;
|
||||
if !attr.flags().contains(SysAttrFlags::CAN_WRITE) {
|
||||
return Err(SysTreeError::PermissionDenied);
|
||||
}
|
||||
// No writable attrs in this mock for now
|
||||
Err(SysTreeError::AttributeError)
|
||||
}
|
||||
}
|
||||
|
||||
impl SysBranchNode for MockBranchNode {
|
||||
fn visit_child_with(&self, name: &str, f: &mut dyn FnMut(Option<&dyn SysNode>)) {
|
||||
self.fields
|
||||
.children
|
||||
.read()
|
||||
.get(name)
|
||||
.map(|child| {
|
||||
child
|
||||
.arc_as_node()
|
||||
.map(|node| f(Some(node.as_ref())))
|
||||
.unwrap_or_else(|| f(None));
|
||||
})
|
||||
.unwrap_or_else(|| f(None));
|
||||
}
|
||||
|
||||
fn visit_children_with(&self, min_id: u64, f: &mut dyn FnMut(&Arc<dyn SysObj>) -> Option<()>) {
|
||||
let children = self.fields.children.read();
|
||||
for child in children
|
||||
.values()
|
||||
.filter(|child| child.id().as_u64() >= min_id)
|
||||
{
|
||||
if f(child).is_none() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn child(&self, name: &str) -> Option<Arc<dyn SysObj>> {
|
||||
let children = self.fields.children.read();
|
||||
children.get(name).cloned()
|
||||
}
|
||||
|
||||
fn children(&self) -> Vec<Arc<dyn SysObj>> {
|
||||
self.fields.children.read().values().cloned().collect()
|
||||
}
|
||||
}
|
||||
|
||||
// Mock Symlink
|
||||
#[derive(Debug)]
|
||||
struct MockSymlinkNode {
|
||||
id: SysNodeId,
|
||||
name: SysStr,
|
||||
target: String,
|
||||
self_ref: Weak<Self>,
|
||||
}
|
||||
|
||||
impl MockSymlinkNode {
|
||||
fn new(name: &str, target: &str) -> Arc<Self> {
|
||||
Arc::new_cyclic(|weak_self| MockSymlinkNode {
|
||||
id: SysNodeId::new(),
|
||||
name: name.to_string().into(),
|
||||
target: target.to_string(),
|
||||
self_ref: weak_self.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl SysObj for MockSymlinkNode {
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
fn arc_as_symlink(&self) -> Option<Arc<dyn SysSymlink>> {
|
||||
self.self_ref
|
||||
.upgrade()
|
||||
.map(|arc_self| arc_self as Arc<dyn SysSymlink>)
|
||||
}
|
||||
fn id(&self) -> &SysNodeId {
|
||||
&self.id
|
||||
}
|
||||
fn type_(&self) -> SysNodeType {
|
||||
SysNodeType::Symlink
|
||||
}
|
||||
fn name(&self) -> SysStr {
|
||||
self.name.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl SysSymlink for MockSymlinkNode {
|
||||
fn target_path(&self) -> &str {
|
||||
&self.target
|
||||
}
|
||||
}
|
||||
|
||||
// --- Test Setup ---
|
||||
|
||||
// Create a mock SysTree instance populated with mock nodes.
|
||||
fn create_mock_systree_instance() -> &'static Arc<SysTree> {
|
||||
time_init_for_ktest();
|
||||
init_for_ktest();
|
||||
// Create nodes
|
||||
let root = systree_singleton().root();
|
||||
let branch1 = MockBranchNode::new("branch1");
|
||||
let leaf1 = MockLeafNode::new("leaf1", &["r_attr1"], &["rw_attr1"]);
|
||||
let leaf2 = MockLeafNode::new("leaf2", &["r_attr2"], &[]);
|
||||
let symlink1 = MockSymlinkNode::new("link1", "../branch1/leaf1");
|
||||
|
||||
// Build hierarchy - ignore Result since this is test setup
|
||||
let _ = branch1.add_child(leaf1.clone() as Arc<dyn SysObj>);
|
||||
let _ = root.add_child(branch1.clone() as Arc<dyn SysObj>);
|
||||
let _ = root.add_child(leaf2.clone() as Arc<dyn SysObj>);
|
||||
let _ = root.add_child(symlink1.clone() as Arc<dyn SysObj>);
|
||||
|
||||
systree_singleton()
|
||||
}
|
||||
|
||||
// Initialize a SysFs instance using the mock systree.
|
||||
fn init_sysfs_with_mock_tree() -> Arc<SysFs> {
|
||||
let _ = create_mock_systree_instance();
|
||||
SysFs::new()
|
||||
}
|
||||
|
||||
#[ktest]
|
||||
fn test_sysfs_root_lookup() {
|
||||
// Setup: Create SysFs instance backed by the mock systree
|
||||
let sysfs = init_sysfs_with_mock_tree();
|
||||
let root_inode = sysfs.root_inode(); // Get the sysfs root inode
|
||||
|
||||
// Verification: Check that the sysfs root inode corresponds to the mock systree root
|
||||
|
||||
assert_eq!(root_inode.type_(), InodeType::Dir);
|
||||
|
||||
// Lookup existing branch
|
||||
let branch1_inode = root_inode.lookup("branch1").expect("Lookup branch1 failed");
|
||||
assert_eq!(branch1_inode.type_(), InodeType::Dir);
|
||||
|
||||
// Lookup existing leaf (represented as Dir in sysfs)
|
||||
let leaf2_inode = root_inode.lookup("leaf2").expect("Lookup leaf2 failed");
|
||||
assert_eq!(leaf2_inode.type_(), InodeType::Dir);
|
||||
|
||||
// Lookup existing symlink
|
||||
let link1_inode = root_inode.lookup("link1").expect("Lookup link1 failed");
|
||||
assert_eq!(link1_inode.type_(), InodeType::SymLink);
|
||||
|
||||
// Lookup non-existent
|
||||
let result = root_inode.lookup("nonexistent");
|
||||
assert!(result.is_err());
|
||||
|
||||
// Lookup "."
|
||||
let self_inode = root_inode.lookup(".").expect("Lookup . failed");
|
||||
assert_eq!(self_inode.ino(), root_inode.ino());
|
||||
|
||||
// Lookup ".." from root
|
||||
let parent_inode = root_inode.lookup("..").expect("Lookup .. failed");
|
||||
assert_eq!(parent_inode.ino(), root_inode.ino()); // Parent of root is root
|
||||
}
|
||||
|
||||
#[ktest]
|
||||
fn test_sysfs_branch_lookup() {
|
||||
let sysfs = init_sysfs_with_mock_tree();
|
||||
let root_inode = sysfs.root_inode();
|
||||
// Action: Lookup a branch node within sysfs
|
||||
let branch1_inode = root_inode.lookup("branch1").unwrap();
|
||||
|
||||
// Verification: Check lookups within the sysfs branch inode,
|
||||
// ensuring they correctly reflect the children and attributes of the underlying mock systree branch node.
|
||||
|
||||
// Lookup existing leaf inside branch
|
||||
let leaf1_inode = branch1_inode.lookup("leaf1").expect("Lookup leaf1 failed");
|
||||
assert_eq!(leaf1_inode.type_(), InodeType::Dir); // Leaf nodes are Dirs
|
||||
|
||||
// Lookup branch attribute
|
||||
let attr_inode = branch1_inode
|
||||
.lookup("branch_attr")
|
||||
.expect("Lookup branch_attr failed");
|
||||
assert_eq!(attr_inode.type_(), InodeType::File);
|
||||
|
||||
// Lookup non-existent inside branch
|
||||
let result = branch1_inode.lookup("nonexistent_leaf");
|
||||
assert!(result.is_err());
|
||||
|
||||
// Lookup "."
|
||||
let self_inode = branch1_inode.lookup(".").expect("Lookup . failed");
|
||||
assert_eq!(self_inode.ino(), branch1_inode.ino());
|
||||
|
||||
// Lookup ".."
|
||||
let parent_inode = branch1_inode.lookup("..").expect("Lookup .. failed");
|
||||
assert_eq!(parent_inode.ino(), root_inode.ino());
|
||||
}
|
||||
|
||||
#[ktest]
|
||||
fn test_sysfs_leaf_lookup() {
|
||||
let sysfs = init_sysfs_with_mock_tree();
|
||||
let root_inode = sysfs.root_inode();
|
||||
// Action: Lookup a leaf node (represented as a directory in sysfs)
|
||||
let leaf1_inode = root_inode
|
||||
.lookup("branch1")
|
||||
.unwrap()
|
||||
.lookup("leaf1")
|
||||
.unwrap();
|
||||
|
||||
// Verification: Check lookups within the sysfs leaf directory,
|
||||
// ensuring they correctly reflect the attributes of the underlying mock systree leaf node.
|
||||
|
||||
assert_eq!(leaf1_inode.type_(), InodeType::Dir); // Leaf node itself is Dir
|
||||
|
||||
// Lookup existing readable attribute
|
||||
let r_attr_inode = leaf1_inode
|
||||
.lookup("r_attr1")
|
||||
.expect("Lookup r_attr1 failed");
|
||||
assert_eq!(r_attr_inode.type_(), InodeType::File);
|
||||
|
||||
// Lookup existing read-write attribute
|
||||
let rw_attr_inode = leaf1_inode
|
||||
.lookup("rw_attr1")
|
||||
.expect("Lookup rw_attr1 failed");
|
||||
assert_eq!(rw_attr_inode.type_(), InodeType::File);
|
||||
|
||||
// Lookup non-existent attribute
|
||||
let result = leaf1_inode.lookup("nonexistent_attr");
|
||||
assert!(result.is_err());
|
||||
|
||||
// Lookup "."
|
||||
let self_inode = leaf1_inode.lookup(".").expect("Lookup . failed");
|
||||
assert_eq!(self_inode.ino(), leaf1_inode.ino());
|
||||
}
|
||||
|
||||
#[ktest]
|
||||
fn test_sysfs_read_attr() {
|
||||
let sysfs = init_sysfs_with_mock_tree();
|
||||
let root_inode = sysfs.root_inode();
|
||||
let leaf1_dir_inode = root_inode
|
||||
.lookup("branch1")
|
||||
.unwrap()
|
||||
.lookup("leaf1")
|
||||
.unwrap();
|
||||
// Action: Lookup the sysfs file corresponding to a systree attribute
|
||||
let r_attr_inode = leaf1_dir_inode.lookup("r_attr1").unwrap();
|
||||
|
||||
// Verification: Read the sysfs file and check if the content matches
|
||||
// the data provided by the underlying mock systree node's read_attr method.
|
||||
|
||||
let mut buf = [0u8; 64];
|
||||
let mut writer = VmWriter::from(&mut buf[..]).to_fallible();
|
||||
let bytes_read = r_attr_inode
|
||||
.read_at(0, &mut writer)
|
||||
.expect("read_at failed");
|
||||
|
||||
assert!(bytes_read > 0);
|
||||
let content = core::str::from_utf8(&buf[..bytes_read]).unwrap();
|
||||
assert_eq!(content, "val_r_attr1");
|
||||
|
||||
// Reading a directory should fail (expect EINVAL as per inode.rs)
|
||||
let mut writer = VmWriter::from(&mut buf[..]).to_fallible(); // Reset writer
|
||||
let result = leaf1_dir_inode.read_at(0, &mut writer);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[ktest]
|
||||
fn test_sysfs_write_attr() {
|
||||
let sysfs = init_sysfs_with_mock_tree();
|
||||
let root_inode = sysfs.root_inode();
|
||||
let leaf1_dir_inode = root_inode
|
||||
.lookup("branch1")
|
||||
.unwrap()
|
||||
.lookup("leaf1")
|
||||
.unwrap();
|
||||
// Action: Lookup sysfs files for attributes
|
||||
let rw_attr_inode = leaf1_dir_inode.lookup("rw_attr1").unwrap();
|
||||
let r_attr_inode = leaf1_dir_inode.lookup("r_attr1").unwrap();
|
||||
|
||||
// Verification: Write to the sysfs files and check if the operation
|
||||
// is correctly delegated to the underlying mock systree node's write_attr method,
|
||||
// respecting read/write permissions derived from SysAttrFlags.
|
||||
|
||||
// Write to rw_attr1
|
||||
let new_val = "new_value";
|
||||
let mut reader = VmReader::from(new_val.as_bytes()).to_fallible();
|
||||
let bytes_written = rw_attr_inode
|
||||
.write_at(0, &mut reader)
|
||||
.expect("write_at failed");
|
||||
assert_eq!(bytes_written, new_val.len());
|
||||
|
||||
// Read back to verify
|
||||
let mut buf = [0u8; 64];
|
||||
let mut writer = VmWriter::from(&mut buf[..]).to_fallible();
|
||||
let bytes_read = rw_attr_inode
|
||||
.read_at(0, &mut writer)
|
||||
.expect("read_at failed");
|
||||
let content = core::str::from_utf8(&buf[..bytes_read]).unwrap();
|
||||
assert_eq!(content, new_val);
|
||||
|
||||
// Write to r_attr1 (should fail - EIO expected from underlying PermissionDenied)
|
||||
let mut reader = VmReader::from("attempt_write".as_bytes()).to_fallible();
|
||||
let result = r_attr_inode.write_at(0, &mut reader);
|
||||
assert!(result.is_err());
|
||||
|
||||
// Writing to a directory should fail (expect EINVAL as per inode.rs)
|
||||
let mut reader = VmReader::from("attempt_write".as_bytes()).to_fallible();
|
||||
let result = leaf1_dir_inode.write_at(0, &mut reader);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[ktest]
|
||||
fn test_sysfs_read_link() {
|
||||
let sysfs = init_sysfs_with_mock_tree();
|
||||
let root_inode = sysfs.root_inode();
|
||||
// Action: Lookup the sysfs symlink corresponding to a systree symlink node
|
||||
let link1_inode = root_inode.lookup("link1").unwrap();
|
||||
|
||||
// Verification: Read the sysfs symlink and check if the target path matches
|
||||
// the path provided by the underlying mock systree symlink node's target_path method.
|
||||
|
||||
let target = link1_inode.read_link().expect("read_link failed");
|
||||
assert_eq!(target, "../branch1/leaf1");
|
||||
|
||||
// read_link on non-symlink should fail (expect EINVAL as per inode.rs)
|
||||
let branch1_inode = root_inode.lookup("branch1").unwrap();
|
||||
let result = branch1_inode.read_link();
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
// Helper for readdir tests
|
||||
struct TestDirentVisitor {
|
||||
entries: Vec<(String, u64, InodeType)>,
|
||||
}
|
||||
|
||||
impl DirentVisitor for TestDirentVisitor {
|
||||
fn visit(&mut self, name: &str, ino: u64, type_: InodeType, _next_offset: usize) -> Result<()> {
|
||||
self.entries.push((name.to_string(), ino, type_));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[ktest]
|
||||
fn test_sysfs_readdir_leaf() {
|
||||
let sysfs = init_sysfs_with_mock_tree();
|
||||
let root_inode = sysfs.root_inode();
|
||||
let leaf1_inode = root_inode
|
||||
.lookup("branch1")
|
||||
.unwrap()
|
||||
.lookup("leaf1")
|
||||
.unwrap(); // The sysfs dir for the leaf node
|
||||
let mut visitor = TestDirentVisitor { entries: vec![] };
|
||||
|
||||
// Action: Read directory entries from the sysfs directory representing a systree leaf node
|
||||
|
||||
let mut offset = 0;
|
||||
loop {
|
||||
// Pass offset as usize
|
||||
let result = leaf1_inode.readdir_at(offset, &mut visitor);
|
||||
match result {
|
||||
Ok(next_offset) => {
|
||||
if next_offset == offset || next_offset == 0 {
|
||||
// Check if no progress or end
|
||||
break;
|
||||
}
|
||||
offset = next_offset;
|
||||
}
|
||||
Err(e) => {
|
||||
panic!("readdir_at failed unexpectedly: {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut names: Vec<_> = visitor.entries.iter().map(|(n, _, _)| n.clone()).collect();
|
||||
names.sort();
|
||||
|
||||
assert!(names.contains(&".".to_string()));
|
||||
assert!(names.contains(&"..".to_string()));
|
||||
assert!(names.contains(&"r_attr1".to_string()));
|
||||
assert!(names.contains(&"rw_attr1".to_string()));
|
||||
|
||||
for (name, _, type_) in &visitor.entries {
|
||||
match name.as_str() {
|
||||
"." | ".." => assert_eq!(*type_, InodeType::Dir),
|
||||
"r_attr1" | "rw_attr1" => assert_eq!(*type_, InodeType::File),
|
||||
_ => panic!("Unexpected entry: {}", name),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[ktest]
|
||||
fn test_sysfs_mode_permissions() {
|
||||
let sysfs = init_sysfs_with_mock_tree();
|
||||
let root_inode = sysfs.root_inode();
|
||||
let leaf1_dir_inode = root_inode
|
||||
.lookup("branch1")
|
||||
.unwrap()
|
||||
.lookup("leaf1")
|
||||
.unwrap();
|
||||
let r_attr_inode = leaf1_dir_inode.lookup("r_attr1").unwrap(); // Sysfs file for read-only attr
|
||||
let rw_attr_inode = leaf1_dir_inode.lookup("rw_attr1").unwrap(); // Sysfs file for read-write attr
|
||||
|
||||
// Verification: Check that the default mode (permissions) of the sysfs files/dirs
|
||||
// correctly reflects the SysAttrFlags of the underlying systree attributes/nodes.
|
||||
// Also test that set_mode works on the sysfs inode.
|
||||
|
||||
// Check default modes based on SysAttrFlags
|
||||
let r_mode = r_attr_inode.mode().unwrap();
|
||||
assert!(r_mode.contains(InodeMode::S_IRUSR | InodeMode::S_IRGRP | InodeMode::S_IROTH)); // 0o444
|
||||
assert!(!r_mode.contains(InodeMode::S_IWUSR | InodeMode::S_IWGRP | InodeMode::S_IWOTH)); // Not 0o222
|
||||
|
||||
let rw_mode = rw_attr_inode.mode().unwrap();
|
||||
assert!(rw_mode.contains(InodeMode::S_IRUSR | InodeMode::S_IRGRP | InodeMode::S_IROTH)); // 0o444
|
||||
assert!(rw_mode.contains(InodeMode::S_IWUSR | InodeMode::S_IWGRP | InodeMode::S_IWOTH)); // 0o222
|
||||
|
||||
// Test set_mode
|
||||
let new_mode = InodeMode::from_bits_truncate(0o600); // rw-------
|
||||
rw_attr_inode.set_mode(new_mode).expect("set_mode failed");
|
||||
assert_eq!(rw_attr_inode.mode().unwrap(), new_mode);
|
||||
|
||||
// Directories should have default mode (e.g., 0o555)
|
||||
let leaf1_mode = leaf1_dir_inode.mode().unwrap();
|
||||
assert!(leaf1_mode.contains(InodeMode::S_IRUSR | InodeMode::S_IXUSR)); // Read/execute for user
|
||||
assert!(leaf1_mode.contains(InodeMode::S_IRGRP | InodeMode::S_IXGRP)); // Read/execute for group
|
||||
assert!(leaf1_mode.contains(InodeMode::S_IROTH | InodeMode::S_IXOTH)); // Read/execute for other
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user