use opensmiles::{parse, BondType, ParserError};
#[test]
fn parse_simple_branch() {
let molecule = parse("CC(C)C").expect("Failed to parse isobutane");
assert_eq!(molecule.nodes().len(), 4);
assert_eq!(molecule.bonds().len(), 3);
let bonds: Vec<_> = molecule.bonds().iter().collect();
assert_eq!(bonds[0].source(), 0);
assert_eq!(bonds[0].target(), 1);
assert_eq!(bonds[1].source(), 1);
assert_eq!(bonds[1].target(), 2);
assert_eq!(bonds[2].source(), 1);
assert_eq!(bonds[2].target(), 3);
}
#[test]
fn parse_multiple_branches() {
let molecule = parse("CC(C)(C)C").expect("Failed to parse neopentane");
assert_eq!(molecule.nodes().len(), 5);
assert_eq!(molecule.bonds().len(), 4);
let central_bonds: Vec<_> = molecule
.bonds()
.iter()
.filter(|b| b.source() == 1 || b.target() == 1)
.collect();
assert_eq!(central_bonds.len(), 4);
}
#[test]
fn parse_nested_branches() {
let molecule = parse("CC(C(C)C)C").expect("Failed to parse 2,3-dimethylbutane");
assert_eq!(molecule.nodes().len(), 6);
assert_eq!(molecule.bonds().len(), 5);
}
#[test]
fn parse_branch_with_double_bond() {
let molecule = parse("CC(=O)O").expect("Failed to parse acetic acid");
assert_eq!(molecule.nodes().len(), 4);
assert_eq!(molecule.bonds().len(), 3);
let double_bond = molecule
.bonds()
.iter()
.find(|b| b.kind() == BondType::Double);
assert!(double_bond.is_some(), "Should have a double bond");
}
#[test]
fn parse_branch_with_triple_bond() {
let molecule = parse("CC(C#N)C").expect("Failed to parse molecule with triple bond in branch");
let triple_bond = molecule
.bonds()
.iter()
.find(|b| b.kind() == BondType::Triple);
assert!(triple_bond.is_some(), "Should have a triple bond");
}
#[test]
fn parse_long_branch() {
let molecule = parse("C(CCCC)C").expect("Failed to parse molecule with long branch");
assert_eq!(molecule.nodes().len(), 6);
assert_eq!(molecule.bonds().len(), 5);
}
#[test]
fn parse_branch_at_start() {
let molecule = parse("(C)CC").expect("Failed to parse branch at start");
assert_eq!(molecule.nodes().len(), 3);
assert_eq!(molecule.bonds().len(), 2);
}
#[test]
fn parse_empty_branch() {
let result = parse("C()C");
assert!(result.is_err(), "Empty branches should not be allowed");
}
#[test]
fn error_position_in_branch() {
match parse("C([C+X])C") {
Err(ParserError::UnexpectedCharacter(c, pos)) => {
assert_eq!(c, 'X', "Expected unexpected character 'X'");
assert_eq!(
pos, 6,
"Position should be absolute (6), not relative to branch"
);
}
Ok(_) => panic!("Expected error, but parsing succeeded"),
Err(other) => panic!("Expected UnexpectedCharacter, got {:?}", other),
}
}
#[test]
fn parse_dendrimer_generation_1() {
let molecule = parse("C(C)(C)").expect("Failed to parse dendrimer gen 1");
assert_eq!(molecule.nodes().len(), 3, "Should have 3 atoms");
assert_eq!(molecule.bonds().len(), 2, "Should have 2 bonds");
let bonds: Vec<_> = molecule.bonds().iter().collect();
assert_eq!(bonds[0].source(), 0);
assert_eq!(bonds[0].target(), 1);
assert_eq!(bonds[1].source(), 0);
assert_eq!(bonds[1].target(), 2);
}
#[test]
fn parse_dendrimer_generation_2() {
let molecule = parse("C(C(C)(C))(C(C)(C))").expect("Failed to parse dendrimer gen 2");
assert_eq!(molecule.nodes().len(), 7, "Should have 7 atoms (2^3 - 1)");
assert_eq!(molecule.bonds().len(), 6, "Should have 6 bonds");
}
#[test]
fn parse_dendrimer_generation_3() {
let smiles = "C(C(C(C)(C))(C(C)(C)))(C(C(C)(C))(C(C)(C)))";
let molecule = parse(smiles).expect("Failed to parse dendrimer gen 3");
assert_eq!(molecule.nodes().len(), 15, "Should have 15 atoms (2^4 - 1)");
assert_eq!(molecule.bonds().len(), 14, "Should have 14 bonds");
}
#[test]
fn parse_dendrimer_generation_4() {
let smiles = "C(C(C(C(C)(C))(C(C)(C)))(C(C(C)(C))(C(C)(C))))(C(C(C(C)(C))(C(C)(C)))(C(C(C)(C))(C(C)(C))))";
let molecule = parse(smiles).expect("Failed to parse dendrimer gen 4");
assert_eq!(molecule.nodes().len(), 31, "Should have 31 atoms (2^5 - 1)");
assert_eq!(molecule.bonds().len(), 30, "Should have 30 bonds");
}
#[test]
fn parse_deeply_nested_linear_branch() {
let molecule = parse("C(C(C(C(C(C)))))").expect("Failed to parse deeply nested branch");
assert_eq!(molecule.nodes().len(), 6, "Should have 6 atoms");
assert_eq!(molecule.bonds().len(), 5, "Should have 5 bonds");
for (i, bond) in molecule.bonds().iter().enumerate() {
assert_eq!(bond.source() as usize, i, "Source should be sequential");
assert_eq!(bond.target() as usize, i + 1, "Target should be source + 1");
}
}
#[test]
fn parse_star_molecule_10_branches() {
let mut smiles = String::from("C");
for _ in 0..10 {
smiles.push_str("(C)");
}
let molecule = parse(&smiles).expect("Failed to parse star with 10 branches");
assert_eq!(molecule.nodes().len(), 11, "Should have 11 atoms");
assert_eq!(molecule.bonds().len(), 10, "Should have 10 bonds");
for bond in molecule.bonds() {
assert_eq!(bond.source(), 0, "All bonds should start from central atom");
}
}
#[test]
fn parse_comb_polymer_structure() {
let molecule = parse("CC(C)C(C)C(C)C(C)C").expect("Failed to parse comb structure");
assert_eq!(molecule.nodes().len(), 10, "Should have 10 atoms");
assert_eq!(molecule.bonds().len(), 9, "Should have 9 bonds");
}
#[test]
fn parse_branch_with_internal_bonds() {
let molecule = parse("C(CCC)C").expect("Failed to parse branch with internal bonds");
assert_eq!(molecule.nodes().len(), 5, "Should have 5 atoms");
assert_eq!(molecule.bonds().len(), 4, "Should have 4 bonds");
let bonds: Vec<_> = molecule.bonds().iter().collect();
assert_eq!(bonds[0].source(), 0);
assert_eq!(bonds[0].target(), 1);
assert_eq!(bonds[1].source(), 1);
assert_eq!(bonds[1].target(), 2);
assert_eq!(bonds[2].source(), 2);
assert_eq!(bonds[2].target(), 3);
assert_eq!(bonds[3].source(), 0);
assert_eq!(bonds[3].target(), 4);
}