#![allow(clippy::unwrap_used)]
use super::super::SysmlAdapter;
use crate::semantic::resolver::Resolver;
use crate::semantic::symbol_table::{Symbol, SymbolTable};
use crate::syntax::sysml::ast::{Comment, Element, NamespaceDeclaration, SysMLFile};
#[test]
fn test_visit_namespace_creates_package_symbol() {
let mut table = SymbolTable::new();
let mut adapter = SysmlAdapter::new(&mut table);
let file = SysMLFile {
namespace: Some(NamespaceDeclaration {
name: "TestNamespace".to_string(),
span: None,
}),
namespaces: vec![],
elements: vec![],
};
let result = adapter.populate(&file);
assert!(result.is_ok());
let resolver = Resolver::new(&table);
let symbol = resolver.resolve("TestNamespace");
assert!(symbol.is_some());
let Some(Symbol::Package {
documentation: None,
name,
qualified_name,
..
}) = symbol
else {
panic!("Expected Package symbol, got: {symbol:?}");
};
assert_eq!(name, "TestNamespace");
assert_eq!(qualified_name, "TestNamespace");
}
#[test]
fn test_visit_namespace_enters_namespace_scope() {
let mut table = SymbolTable::new();
let mut adapter = SysmlAdapter::new(&mut table);
let file = SysMLFile {
namespace: Some(NamespaceDeclaration {
name: "MyNamespace".to_string(),
span: None,
}),
namespaces: vec![],
elements: vec![],
};
let result = adapter.populate(&file);
assert!(result.is_ok());
let resolver = Resolver::new(&table);
let symbol = resolver.resolve("MyNamespace");
assert!(symbol.is_some());
}
#[test]
fn test_visit_namespace_with_empty_name() {
let mut table = SymbolTable::new();
let mut adapter = SysmlAdapter::new(&mut table);
let file = SysMLFile {
namespace: Some(NamespaceDeclaration {
name: "".to_string(),
span: None,
}),
namespaces: vec![],
elements: vec![],
};
let result = adapter.populate(&file);
assert!(result.is_ok());
let resolver = Resolver::new(&table);
let symbol = resolver.resolve("");
assert!(symbol.is_some());
}
#[test]
fn test_visit_namespace_with_special_characters() {
let mut table = SymbolTable::new();
let mut adapter = SysmlAdapter::new(&mut table);
let file = SysMLFile {
namespace: Some(NamespaceDeclaration {
name: "Name_With_Underscores123".to_string(),
span: None,
}),
namespaces: vec![],
elements: vec![],
};
let result = adapter.populate(&file);
assert!(result.is_ok());
let resolver = Resolver::new(&table);
let symbol = resolver.resolve("Name_With_Underscores123");
assert!(symbol.is_some());
}
#[test]
fn test_visit_namespace_stores_scope_id() {
let mut table = SymbolTable::new();
let mut adapter = SysmlAdapter::new(&mut table);
let file = SysMLFile {
namespace: Some(NamespaceDeclaration {
name: "ScopedNamespace".to_string(),
span: None,
}),
namespaces: vec![],
elements: vec![],
};
let result = adapter.populate(&file);
assert!(result.is_ok());
let resolver = Resolver::new(&table);
let symbol = resolver.resolve("ScopedNamespace");
assert!(symbol.is_some());
if let Some(Symbol::Package {
documentation: None,
scope_id,
..
}) = symbol
{
assert_eq!(*scope_id, 0);
} else {
panic!("Expected Package symbol");
}
}
#[test]
fn test_visit_namespace_with_span() {
use crate::core::{Position, Span};
let mut table = SymbolTable::new();
let mut adapter = SysmlAdapter::new(&mut table);
let test_span = Some(Span {
start: Position::new(1, 1),
end: Position::new(1, 10),
});
let file = SysMLFile {
namespace: Some(NamespaceDeclaration {
name: "SpannedNamespace".to_string(),
span: test_span,
}),
namespaces: vec![],
elements: vec![],
};
let result = adapter.populate(&file);
assert!(result.is_ok());
let resolver = Resolver::new(&table);
let symbol = resolver.resolve("SpannedNamespace");
assert!(symbol.is_some());
if let Some(Symbol::Package {
documentation: None,
span,
..
}) = symbol
{
assert_eq!(*span, test_span);
} else {
panic!("Expected Package symbol");
}
}
#[test]
fn test_visit_namespace_no_namespace_in_file() {
let mut table = SymbolTable::new();
let mut adapter = SysmlAdapter::new(&mut table);
let file = SysMLFile {
namespace: None,
namespaces: vec![],
elements: vec![],
};
let result = adapter.populate(&file);
assert!(result.is_ok());
let is_empty = table.iter_symbols().next().is_none();
let has_empty_name = table.iter_symbols().any(|sym| sym.name().is_empty());
assert!(is_empty || !has_empty_name);
}
#[test]
fn test_visit_namespace_affects_subsequent_elements() {
use crate::syntax::sysml::ast::{Definition, DefinitionKind};
let mut table = SymbolTable::new();
let mut adapter = SysmlAdapter::new(&mut table);
let file = SysMLFile {
namespace: Some(NamespaceDeclaration {
name: "OuterNamespace".to_string(),
span: None,
}),
namespaces: vec![],
elements: vec![Element::Definition(Definition {
kind: DefinitionKind::Part,
name: Some("InnerPart".to_string()),
body: vec![],
relationships: Default::default(),
is_abstract: false,
is_variation: false,
span: None,
short_name: None,
short_name_span: None,
})],
};
let result = adapter.populate(&file);
assert!(result.is_ok());
let resolver = Resolver::new(&table);
let ns_symbol = resolver.resolve("OuterNamespace");
assert!(ns_symbol.is_some());
let has_qualified = table
.iter_symbols()
.any(|sym| sym.qualified_name().contains("OuterNamespace::InnerPart"));
assert!(
has_qualified,
"Definition should be qualified with namespace"
);
}
#[test]
fn test_visit_namespace_multiple_namespaces_not_supported() {
let mut table = SymbolTable::new();
let mut adapter = SysmlAdapter::new(&mut table);
let file = SysMLFile {
namespace: Some(NamespaceDeclaration {
name: "FirstNamespace".to_string(),
span: None,
}),
namespaces: vec![],
elements: vec![],
};
let result = adapter.populate(&file);
assert!(result.is_ok());
let resolver = Resolver::new(&table);
let symbol = resolver.resolve("FirstNamespace");
assert!(symbol.is_some());
}
#[test]
fn test_visit_comment_does_not_affect_symbol_table() {
let mut table = SymbolTable::new();
let mut adapter = SysmlAdapter::new(&mut table);
let file = SysMLFile {
namespace: None,
namespaces: vec![],
elements: vec![Element::Comment(Comment::new(
"This is a test comment",
None,
))],
};
let result = adapter.populate(&file);
assert!(result.is_ok());
assert!(table.iter_symbols().next().is_none());
}
#[test]
fn test_visit_comment_empty_content() {
let mut table = SymbolTable::new();
let mut adapter = SysmlAdapter::new(&mut table);
let file = SysMLFile {
namespace: None,
namespaces: vec![],
elements: vec![Element::Comment(Comment::new("", None))],
};
let result = adapter.populate(&file);
assert!(result.is_ok());
assert!(table.iter_symbols().next().is_none());
}
#[test]
fn test_visit_comment_multiline_content() {
let mut table = SymbolTable::new();
let mut adapter = SysmlAdapter::new(&mut table);
let file = SysMLFile {
namespace: None,
namespaces: vec![],
elements: vec![Element::Comment(Comment::new(
"Line 1\nLine 2\nLine 3",
None,
))],
};
let result = adapter.populate(&file);
assert!(result.is_ok());
assert!(table.iter_symbols().next().is_none());
}
#[test]
fn test_visit_comment_with_special_characters() {
let mut table = SymbolTable::new();
let mut adapter = SysmlAdapter::new(&mut table);
let file = SysMLFile {
namespace: None,
namespaces: vec![],
elements: vec![Element::Comment(Comment::new(
"/* Comment with special chars: @#$%^&*() */",
None,
))],
};
let result = adapter.populate(&file);
assert!(result.is_ok());
assert!(table.iter_symbols().next().is_none());
}
#[test]
fn test_visit_comment_multiple_comments() {
let mut table = SymbolTable::new();
let mut adapter = SysmlAdapter::new(&mut table);
let file = SysMLFile {
namespace: None,
namespaces: vec![],
elements: vec![
Element::Comment(Comment::new("First comment", None)),
Element::Comment(Comment::new("Second comment", None)),
Element::Comment(Comment::new("Third comment", None)),
],
};
let result = adapter.populate(&file);
assert!(result.is_ok());
assert!(table.iter_symbols().next().is_none());
}
#[test]
fn test_visit_comment_between_definitions() {
use crate::syntax::sysml::ast::{Definition, DefinitionKind};
let mut table = SymbolTable::new();
let mut adapter = SysmlAdapter::new(&mut table);
let file = SysMLFile {
namespace: None,
namespaces: vec![],
elements: vec![
Element::Definition(Definition {
kind: DefinitionKind::Part,
name: Some("FirstPart".to_string()),
body: vec![],
relationships: Default::default(),
is_abstract: false,
is_variation: false,
span: None,
short_name: None,
short_name_span: None,
}),
Element::Comment(Comment::new("Comment between definitions", None)),
Element::Definition(Definition {
kind: DefinitionKind::Part,
name: Some("SecondPart".to_string()),
body: vec![],
relationships: Default::default(),
is_abstract: false,
is_variation: false,
span: None,
short_name: None,
short_name_span: None,
}),
],
};
let result = adapter.populate(&file);
assert!(result.is_ok());
assert_eq!(table.iter_symbols().count(), 2);
assert!(Resolver::new(&table).resolve("FirstPart").is_some());
assert!(Resolver::new(&table).resolve("SecondPart").is_some());
}
#[test]
fn test_visit_comment_with_span() {
use crate::core::{Position, Span};
let mut table = SymbolTable::new();
let mut adapter = SysmlAdapter::new(&mut table);
let file = SysMLFile {
namespace: None,
namespaces: vec![],
elements: vec![Element::Comment(Comment::new(
"Comment with span",
Some(Span {
start: Position::new(1, 1),
end: Position::new(1, 20),
}),
))],
};
let result = adapter.populate(&file);
assert!(result.is_ok());
assert!(table.iter_symbols().next().is_none());
}
#[test]
fn test_visit_comment_does_not_change_current_namespace() {
use crate::syntax::sysml::ast::{Definition, DefinitionKind};
let mut table = SymbolTable::new();
let mut adapter = SysmlAdapter::new(&mut table);
let file = SysMLFile {
namespace: Some(NamespaceDeclaration {
name: "TestNS".to_string(),
span: None,
}),
namespaces: vec![],
elements: vec![
Element::Comment(Comment::new("Comment in namespace", None)),
Element::Definition(Definition {
kind: DefinitionKind::Part,
name: Some("PartInNamespace".to_string()),
body: vec![],
relationships: Default::default(),
is_abstract: false,
is_variation: false,
span: None,
short_name: None,
short_name_span: None,
}),
],
};
let result = adapter.populate(&file);
assert!(result.is_ok());
let has_qualified = table
.iter_symbols()
.any(|sym| sym.qualified_name().contains("TestNS::PartInNamespace"));
assert!(has_qualified, "Comment should not affect namespace context");
}
#[test]
fn test_visit_comment_long_content() {
let mut table = SymbolTable::new();
let mut adapter = SysmlAdapter::new(&mut table);
let long_comment = "a".repeat(10000);
let file = SysMLFile {
namespace: None,
namespaces: vec![],
elements: vec![Element::Comment(Comment::new(&long_comment, None))],
};
let result = adapter.populate(&file);
assert!(result.is_ok());
assert!(table.iter_symbols().next().is_none());
}
#[test]
fn test_visit_comment_unicode_content() {
let mut table = SymbolTable::new();
let mut adapter = SysmlAdapter::new(&mut table);
let file = SysMLFile {
namespace: None,
namespaces: vec![],
elements: vec![Element::Comment(Comment::new(
"Unicode comment: 你好世界 🚀 ñ é",
None,
))],
};
let result = adapter.populate(&file);
assert!(result.is_ok());
assert!(table.iter_symbols().next().is_none());
}
#[test]
fn test_namespace_with_comments() {
let mut table = SymbolTable::new();
let mut adapter = SysmlAdapter::new(&mut table);
let file = SysMLFile {
namespace: Some(NamespaceDeclaration {
name: "DocumentedNamespace".to_string(),
span: None,
}),
namespaces: vec![],
elements: vec![Element::Comment(Comment::new(
"This namespace is documented",
None,
))],
};
let result = adapter.populate(&file);
assert!(result.is_ok());
assert_eq!(table.iter_symbols().count(), 1);
assert!(
Resolver::new(&table)
.resolve("DocumentedNamespace")
.is_some()
);
}
#[test]
fn test_comment_before_namespace_not_typical_but_handled() {
let mut table = SymbolTable::new();
let mut adapter = SysmlAdapter::new(&mut table);
let file = SysMLFile {
namespace: Some(NamespaceDeclaration {
name: "LateNamespace".to_string(),
span: None,
}),
namespaces: vec![],
elements: vec![Element::Comment(Comment::new(
"Comment at file level",
None,
))],
};
let result = adapter.populate(&file);
assert!(result.is_ok());
let resolver = Resolver::new(&table);
let symbol = resolver.resolve("LateNamespace");
assert!(symbol.is_some());
assert_eq!(table.iter_symbols().count(), 1);
}