parent
4e3a81d7f5
commit
c9746230ef
|
@ -17,28 +17,27 @@
|
|||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Abstract graph as the basis for concrete ASGs.
|
||||
//! Abstract semantic graph.
|
||||
|
||||
use super::object::{ObjectContainer, ObjectRelTo};
|
||||
use super::{
|
||||
object::{ObjectContainer, ObjectRelTo},
|
||||
AsgError, FragmentText, Ident, IdentKind, Object, ObjectIndex, ObjectKind,
|
||||
Source, TransitionResult,
|
||||
};
|
||||
use crate::diagnose::panic::DiagnosticPanic;
|
||||
use crate::diagnose::{Annotate, AnnotatedSpan};
|
||||
use crate::f::Functor;
|
||||
use crate::fmt::{DisplayWrapper, TtQuote};
|
||||
use crate::global;
|
||||
use crate::parse::util::SPair;
|
||||
use crate::parse::Token;
|
||||
use crate::sym::SymbolId;
|
||||
use crate::{
|
||||
diagnose::{panic::DiagnosticPanic, Annotate, AnnotatedSpan},
|
||||
f::Functor,
|
||||
fmt::{DisplayWrapper, TtQuote},
|
||||
global,
|
||||
parse::{util::SPair, Token},
|
||||
sym::SymbolId,
|
||||
};
|
||||
use petgraph::{
|
||||
graph::{DiGraph, Graph, NodeIndex},
|
||||
visit::EdgeRef,
|
||||
Direction,
|
||||
};
|
||||
use std::fmt::Debug;
|
||||
use std::result::Result;
|
||||
use std::{fmt::Debug, result::Result};
|
||||
|
||||
/// Datatype representing node and edge indexes.
|
||||
pub trait IndexType = petgraph::graph::IndexType;
|
||||
|
@ -702,408 +701,4 @@ fn diagnostic_opaque_ident_desc(ident: SPair) -> Vec<AnnotatedSpan<'static>> {
|
|||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::super::error::AsgError;
|
||||
use super::*;
|
||||
use crate::{num::Dim, span::dummy::*, sym::GlobalSymbolIntern};
|
||||
use std::{assert_matches::assert_matches, convert::Infallible};
|
||||
|
||||
type Sut = Asg;
|
||||
|
||||
#[test]
|
||||
fn create_with_capacity() {
|
||||
let node_capacity = 100;
|
||||
let edge_capacity = 300;
|
||||
let sut = Sut::with_capacity(node_capacity, edge_capacity);
|
||||
|
||||
let (nc, ec) = sut.graph.capacity();
|
||||
assert!(nc >= node_capacity);
|
||||
assert!(ec >= edge_capacity);
|
||||
assert!(sut.index.capacity() >= node_capacity);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn declare_new_unique_idents() -> AsgResult<()> {
|
||||
let mut sut = Sut::new();
|
||||
|
||||
// NB: The index ordering is important! We first use a larger
|
||||
// index to create a gap, and then use an index within that gap
|
||||
// to ensure that it's not considered an already-defined
|
||||
// identifier.
|
||||
let syma = "syma".into();
|
||||
let symb = "symab".into();
|
||||
|
||||
let nodea = sut.declare(
|
||||
SPair(syma, S1),
|
||||
IdentKind::Meta,
|
||||
Source {
|
||||
desc: Some("a".into()),
|
||||
..Default::default()
|
||||
},
|
||||
)?;
|
||||
|
||||
let nodeb = sut.declare(
|
||||
SPair(symb, S2),
|
||||
IdentKind::Worksheet,
|
||||
Source {
|
||||
desc: Some("b".into()),
|
||||
..Default::default()
|
||||
},
|
||||
)?;
|
||||
|
||||
assert_ne!(nodea, nodeb);
|
||||
|
||||
let givena = sut.get_ident(nodea).unwrap();
|
||||
assert_eq!(SPair(syma, S1), givena.name());
|
||||
assert_eq!(Some(&IdentKind::Meta), givena.kind());
|
||||
assert_eq!(
|
||||
Some(&Source {
|
||||
desc: Some("a".into()),
|
||||
..Default::default()
|
||||
},),
|
||||
givena.src()
|
||||
);
|
||||
|
||||
let givenb = sut.get_ident(nodeb).unwrap();
|
||||
assert_eq!(SPair(symb, S2), givenb.name());
|
||||
assert_eq!(Some(&IdentKind::Worksheet), givenb.kind());
|
||||
assert_eq!(
|
||||
Some(&Source {
|
||||
desc: Some("b".into()),
|
||||
..Default::default()
|
||||
}),
|
||||
givenb.src()
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn declare_kind_auto_root() -> AsgResult<()> {
|
||||
let mut sut = Sut::new();
|
||||
|
||||
let auto_kind = IdentKind::Worksheet;
|
||||
// Sanity check, in case this changes.
|
||||
assert!(auto_kind.is_auto_root());
|
||||
|
||||
let auto_root_node = sut.declare(
|
||||
SPair("auto_root".into(), S1),
|
||||
auto_kind,
|
||||
Default::default(),
|
||||
)?;
|
||||
|
||||
// Should have been automatically added as a root.
|
||||
assert!(sut
|
||||
.graph
|
||||
.contains_edge(sut.root_node, auto_root_node.into()));
|
||||
|
||||
let no_auto_kind = IdentKind::Tpl;
|
||||
assert!(!no_auto_kind.is_auto_root());
|
||||
|
||||
let no_auto_root_node = sut.declare(
|
||||
SPair("no_auto_root".into(), S2),
|
||||
no_auto_kind,
|
||||
Default::default(),
|
||||
)?;
|
||||
|
||||
// Non-auto-roots should _not_ be added as roots automatically.
|
||||
assert!(!sut
|
||||
.graph
|
||||
.contains_edge(sut.root_node, no_auto_root_node.into()));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lookup_by_symbol() -> AsgResult<()> {
|
||||
let mut sut = Sut::new();
|
||||
|
||||
let id = SPair("lookup".into(), S1);
|
||||
let node = sut.declare(
|
||||
id,
|
||||
IdentKind::Meta,
|
||||
Source {
|
||||
generated: true,
|
||||
..Default::default()
|
||||
},
|
||||
)?;
|
||||
|
||||
assert_eq!(Some(node), sut.lookup(id));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn declare_fails_if_transition_fails() -> AsgResult<()> {
|
||||
let mut sut = Sut::new();
|
||||
|
||||
let sym = "symdup".into();
|
||||
let src = Source {
|
||||
desc: Some("orig".into()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
// Set up an object to fail redeclaration.
|
||||
let node = sut.declare(SPair(sym, S1), IdentKind::Meta, src.clone())?;
|
||||
let result =
|
||||
sut.declare(SPair(sym, S2), IdentKind::Meta, Source::default());
|
||||
|
||||
assert_matches!(result, Err(AsgError::IdentTransition(..)));
|
||||
|
||||
// The node should have been restored.
|
||||
assert_eq!(Some(&src), sut.get_ident(node).unwrap().src());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn declare_extern_returns_existing() -> AsgResult<()> {
|
||||
let mut sut = Sut::new();
|
||||
|
||||
let sym = "symext".into();
|
||||
let src = Source::default();
|
||||
let kind = IdentKind::Class(Dim::Matrix);
|
||||
let node =
|
||||
sut.declare_extern(SPair(sym, S1), kind.clone(), src.clone())?;
|
||||
|
||||
let resrc = Source {
|
||||
desc: Some("redeclare".into()),
|
||||
..Default::default()
|
||||
};
|
||||
let redeclare =
|
||||
sut.declare_extern(SPair(sym, S2), kind.clone(), resrc.clone())?;
|
||||
|
||||
assert_eq!(node, redeclare);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Builds upon declare_returns_existing.
|
||||
#[test]
|
||||
fn declare_extern_fails_if_transition_fails() -> AsgResult<()> {
|
||||
let mut sut = Sut::new();
|
||||
|
||||
let sym = "symdup".into();
|
||||
let src = Source {
|
||||
desc: Some("orig".into()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let node = sut.declare(SPair(sym, S1), IdentKind::Meta, src.clone())?;
|
||||
|
||||
// Changes kind, which is invalid.
|
||||
let result = sut.declare_extern(
|
||||
SPair(sym, S2),
|
||||
IdentKind::Worksheet,
|
||||
Source::default(),
|
||||
);
|
||||
|
||||
assert_matches!(result, Err(AsgError::IdentTransition(..)));
|
||||
|
||||
// The node should have been restored.
|
||||
assert_eq!(Some(&src), sut.get_ident(node).unwrap().src());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_fragment_to_ident() -> AsgResult<()> {
|
||||
let mut sut = Sut::new();
|
||||
|
||||
let sym = "tofrag".into();
|
||||
let src = Source {
|
||||
generated: true,
|
||||
..Default::default()
|
||||
};
|
||||
let node = sut.declare(SPair(sym, S1), IdentKind::Meta, src.clone())?;
|
||||
|
||||
let fragment = "a fragment".intern();
|
||||
let node_with_frag = sut.set_fragment(SPair(sym, S2), fragment)?;
|
||||
|
||||
// Attaching a fragment should _replace_ the node, not create a
|
||||
// new one
|
||||
assert_eq!(
|
||||
node, node_with_frag,
|
||||
"fragment node does not match original node"
|
||||
);
|
||||
|
||||
let obj = sut.get_ident(node).unwrap();
|
||||
|
||||
assert_eq!(SPair(sym, S1), obj.name());
|
||||
assert_eq!(Some(&IdentKind::Meta), obj.kind());
|
||||
assert_eq!(Some(&src), obj.src());
|
||||
assert_eq!(Some(fragment), obj.fragment());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_fragment_to_ident_fails_if_transition_fails() -> AsgResult<()> {
|
||||
let mut sut = Sut::new();
|
||||
|
||||
let sym = "failfrag".into();
|
||||
let src = Source {
|
||||
generated: true,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
// The failure will come from terr below, not this.
|
||||
let node = sut.declare(SPair(sym, S1), IdentKind::Meta, src.clone())?;
|
||||
|
||||
// The first set will succeed.
|
||||
sut.set_fragment(SPair(sym, S2), "".into())?;
|
||||
|
||||
// This will fail.
|
||||
let result = sut.set_fragment(SPair(sym, S3), "".into());
|
||||
|
||||
// The node should have been restored.
|
||||
let obj = sut.get_ident(node).unwrap();
|
||||
|
||||
assert_eq!(SPair(sym, S1), obj.name());
|
||||
assert_matches!(result, Err(AsgError::IdentTransition(..)));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_ident_dep_to_ident() -> AsgResult<()> {
|
||||
let mut sut = Sut::new();
|
||||
|
||||
let sym = "sym".into();
|
||||
let dep = "dep".into();
|
||||
|
||||
let symnode =
|
||||
sut.declare(SPair(sym, S1), IdentKind::Meta, Source::default())?;
|
||||
let depnode =
|
||||
sut.declare(SPair(dep, S2), IdentKind::Meta, Source::default())?;
|
||||
|
||||
sut.add_dep(symnode, depnode);
|
||||
assert!(sut.has_dep(symnode, depnode));
|
||||
|
||||
// sanity check if we re-add a dep
|
||||
sut.add_dep(symnode, depnode);
|
||||
assert!(sut.has_dep(symnode, depnode));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// same as above test
|
||||
#[test]
|
||||
fn add_dep_lookup_existing() -> AsgResult<()> {
|
||||
let mut sut = Sut::new();
|
||||
|
||||
let sym = SPair("sym".into(), S1);
|
||||
let dep = SPair("dep".into(), S2);
|
||||
|
||||
let _ = sut.declare(sym, IdentKind::Meta, Source::default())?;
|
||||
let _ = sut.declare(dep, IdentKind::Meta, Source::default())?;
|
||||
|
||||
let (symnode, depnode) = sut.add_dep_lookup(sym, dep);
|
||||
assert!(sut.has_dep(symnode, depnode));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_dep_lookup_missing() -> AsgResult<()> {
|
||||
let mut sut = Sut::new();
|
||||
|
||||
let sym = SPair("sym".into(), S1);
|
||||
let dep = SPair("dep".into(), S2);
|
||||
|
||||
// both of these are missing
|
||||
let (symnode, depnode) = sut.add_dep_lookup(sym, dep);
|
||||
assert!(sut.has_dep(symnode, depnode));
|
||||
|
||||
assert_eq!(sym, sut.get_ident(symnode).unwrap().name());
|
||||
assert_eq!(dep, sut.get_ident(depnode).unwrap().name());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn declare_return_missing_symbol() -> AsgResult<()> {
|
||||
let mut sut = Sut::new();
|
||||
|
||||
let sym = SPair("sym".into(), S1);
|
||||
let dep = SPair("dep".into(), S2);
|
||||
|
||||
// both of these are missing, see add_dep_lookup_missing
|
||||
let (symnode, _) = sut.add_dep_lookup(sym, dep);
|
||||
|
||||
let src = Source {
|
||||
desc: Some("redeclare missing".into()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
// Check with a declared value
|
||||
let declared = sut.declare(sym, IdentKind::Meta, src.clone())?;
|
||||
|
||||
assert_eq!(symnode, declared);
|
||||
|
||||
let obj = sut.get_ident(declared).unwrap();
|
||||
|
||||
assert_eq!(sym, obj.name());
|
||||
assert_eq!(Some(&IdentKind::Meta), obj.kind());
|
||||
assert_eq!(Some(&src), obj.src());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn try_map_narrows_and_modifies() {
|
||||
let mut sut = Sut::new();
|
||||
|
||||
let id_a = SPair("foo".into(), S1);
|
||||
let id_b = SPair("bar".into(), S2);
|
||||
|
||||
let oi = sut.create(Ident::Missing(id_a));
|
||||
|
||||
// This is the method under test.
|
||||
// It should narrow to an `Ident` because `oi` was `create`'d with
|
||||
// an `Ident`.
|
||||
let oi_new = sut
|
||||
.try_map_obj(oi, |ident| {
|
||||
assert_eq!(ident, Ident::Missing(id_a));
|
||||
|
||||
// Replace the identifier
|
||||
Ok::<_, (_, Infallible)>(Ident::Missing(id_b))
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
// These would not typically be checked by the caller;
|
||||
// they are intended for debugging.
|
||||
assert_eq!(S1, oi.into());
|
||||
assert_eq!(S2, oi_new.into());
|
||||
|
||||
// A change in span does not change its equivalence.
|
||||
assert_eq!(oi_new, oi);
|
||||
|
||||
// Ensure that the graph was updated with the new object from the
|
||||
// above method.
|
||||
assert_eq!(&Ident::Missing(id_b), sut.get(oi).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn try_map_failure_restores_original_object() {
|
||||
let mut sut = Sut::new();
|
||||
|
||||
let id_a = SPair("foo".into(), S1);
|
||||
|
||||
let err = "uh oh";
|
||||
|
||||
let oi = sut.create(Ident::Missing(id_a));
|
||||
|
||||
// This will fail to modify the object.
|
||||
let oi_new = sut.try_map_obj(oi, |ident| {
|
||||
assert_eq!(ident, Ident::Missing(id_a));
|
||||
|
||||
Err((ident, err))
|
||||
});
|
||||
|
||||
assert_eq!(Err(err), oi_new);
|
||||
|
||||
// Ensure that the original object was retained.
|
||||
assert_eq!(&Ident::Missing(id_a), sut.get(oi).unwrap());
|
||||
}
|
||||
}
|
||||
mod test;
|
||||
|
|
|
@ -0,0 +1,421 @@
|
|||
// Tests for graph abstraction
|
||||
//
|
||||
// Copyright (C) 2014-2022 Ryan Specialty Group, LLC.
|
||||
//
|
||||
// This file is part of TAME.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use super::super::error::AsgError;
|
||||
use super::*;
|
||||
use crate::{num::Dim, span::dummy::*, sym::GlobalSymbolIntern};
|
||||
use std::{assert_matches::assert_matches, convert::Infallible};
|
||||
|
||||
type Sut = Asg;
|
||||
|
||||
#[test]
|
||||
fn create_with_capacity() {
|
||||
let node_capacity = 100;
|
||||
let edge_capacity = 300;
|
||||
let sut = Sut::with_capacity(node_capacity, edge_capacity);
|
||||
|
||||
let (nc, ec) = sut.graph.capacity();
|
||||
assert!(nc >= node_capacity);
|
||||
assert!(ec >= edge_capacity);
|
||||
assert!(sut.index.capacity() >= node_capacity);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn declare_new_unique_idents() -> AsgResult<()> {
|
||||
let mut sut = Sut::new();
|
||||
|
||||
// NB: The index ordering is important! We first use a larger
|
||||
// index to create a gap, and then use an index within that gap
|
||||
// to ensure that it's not considered an already-defined
|
||||
// identifier.
|
||||
let syma = "syma".into();
|
||||
let symb = "symab".into();
|
||||
|
||||
let nodea = sut.declare(
|
||||
SPair(syma, S1),
|
||||
IdentKind::Meta,
|
||||
Source {
|
||||
desc: Some("a".into()),
|
||||
..Default::default()
|
||||
},
|
||||
)?;
|
||||
|
||||
let nodeb = sut.declare(
|
||||
SPair(symb, S2),
|
||||
IdentKind::Worksheet,
|
||||
Source {
|
||||
desc: Some("b".into()),
|
||||
..Default::default()
|
||||
},
|
||||
)?;
|
||||
|
||||
assert_ne!(nodea, nodeb);
|
||||
|
||||
let givena = sut.get_ident(nodea).unwrap();
|
||||
assert_eq!(SPair(syma, S1), givena.name());
|
||||
assert_eq!(Some(&IdentKind::Meta), givena.kind());
|
||||
assert_eq!(
|
||||
Some(&Source {
|
||||
desc: Some("a".into()),
|
||||
..Default::default()
|
||||
},),
|
||||
givena.src()
|
||||
);
|
||||
|
||||
let givenb = sut.get_ident(nodeb).unwrap();
|
||||
assert_eq!(SPair(symb, S2), givenb.name());
|
||||
assert_eq!(Some(&IdentKind::Worksheet), givenb.kind());
|
||||
assert_eq!(
|
||||
Some(&Source {
|
||||
desc: Some("b".into()),
|
||||
..Default::default()
|
||||
}),
|
||||
givenb.src()
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn declare_kind_auto_root() -> AsgResult<()> {
|
||||
let mut sut = Sut::new();
|
||||
|
||||
let auto_kind = IdentKind::Worksheet;
|
||||
// Sanity check, in case this changes.
|
||||
assert!(auto_kind.is_auto_root());
|
||||
|
||||
let auto_root_node = sut.declare(
|
||||
SPair("auto_root".into(), S1),
|
||||
auto_kind,
|
||||
Default::default(),
|
||||
)?;
|
||||
|
||||
// Should have been automatically added as a root.
|
||||
assert!(sut
|
||||
.graph
|
||||
.contains_edge(sut.root_node, auto_root_node.into()));
|
||||
|
||||
let no_auto_kind = IdentKind::Tpl;
|
||||
assert!(!no_auto_kind.is_auto_root());
|
||||
|
||||
let no_auto_root_node = sut.declare(
|
||||
SPair("no_auto_root".into(), S2),
|
||||
no_auto_kind,
|
||||
Default::default(),
|
||||
)?;
|
||||
|
||||
// Non-auto-roots should _not_ be added as roots automatically.
|
||||
assert!(!sut
|
||||
.graph
|
||||
.contains_edge(sut.root_node, no_auto_root_node.into()));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lookup_by_symbol() -> AsgResult<()> {
|
||||
let mut sut = Sut::new();
|
||||
|
||||
let id = SPair("lookup".into(), S1);
|
||||
let node = sut.declare(
|
||||
id,
|
||||
IdentKind::Meta,
|
||||
Source {
|
||||
generated: true,
|
||||
..Default::default()
|
||||
},
|
||||
)?;
|
||||
|
||||
assert_eq!(Some(node), sut.lookup(id));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn declare_fails_if_transition_fails() -> AsgResult<()> {
|
||||
let mut sut = Sut::new();
|
||||
|
||||
let sym = "symdup".into();
|
||||
let src = Source {
|
||||
desc: Some("orig".into()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
// Set up an object to fail redeclaration.
|
||||
let node = sut.declare(SPair(sym, S1), IdentKind::Meta, src.clone())?;
|
||||
let result =
|
||||
sut.declare(SPair(sym, S2), IdentKind::Meta, Source::default());
|
||||
|
||||
assert_matches!(result, Err(AsgError::IdentTransition(..)));
|
||||
|
||||
// The node should have been restored.
|
||||
assert_eq!(Some(&src), sut.get_ident(node).unwrap().src());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn declare_extern_returns_existing() -> AsgResult<()> {
|
||||
let mut sut = Sut::new();
|
||||
|
||||
let sym = "symext".into();
|
||||
let src = Source::default();
|
||||
let kind = IdentKind::Class(Dim::Matrix);
|
||||
let node = sut.declare_extern(SPair(sym, S1), kind.clone(), src.clone())?;
|
||||
|
||||
let resrc = Source {
|
||||
desc: Some("redeclare".into()),
|
||||
..Default::default()
|
||||
};
|
||||
let redeclare =
|
||||
sut.declare_extern(SPair(sym, S2), kind.clone(), resrc.clone())?;
|
||||
|
||||
assert_eq!(node, redeclare);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Builds upon declare_returns_existing.
|
||||
#[test]
|
||||
fn declare_extern_fails_if_transition_fails() -> AsgResult<()> {
|
||||
let mut sut = Sut::new();
|
||||
|
||||
let sym = "symdup".into();
|
||||
let src = Source {
|
||||
desc: Some("orig".into()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let node = sut.declare(SPair(sym, S1), IdentKind::Meta, src.clone())?;
|
||||
|
||||
// Changes kind, which is invalid.
|
||||
let result = sut.declare_extern(
|
||||
SPair(sym, S2),
|
||||
IdentKind::Worksheet,
|
||||
Source::default(),
|
||||
);
|
||||
|
||||
assert_matches!(result, Err(AsgError::IdentTransition(..)));
|
||||
|
||||
// The node should have been restored.
|
||||
assert_eq!(Some(&src), sut.get_ident(node).unwrap().src());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_fragment_to_ident() -> AsgResult<()> {
|
||||
let mut sut = Sut::new();
|
||||
|
||||
let sym = "tofrag".into();
|
||||
let src = Source {
|
||||
generated: true,
|
||||
..Default::default()
|
||||
};
|
||||
let node = sut.declare(SPair(sym, S1), IdentKind::Meta, src.clone())?;
|
||||
|
||||
let fragment = "a fragment".intern();
|
||||
let node_with_frag = sut.set_fragment(SPair(sym, S2), fragment)?;
|
||||
|
||||
// Attaching a fragment should _replace_ the node, not create a
|
||||
// new one
|
||||
assert_eq!(
|
||||
node, node_with_frag,
|
||||
"fragment node does not match original node"
|
||||
);
|
||||
|
||||
let obj = sut.get_ident(node).unwrap();
|
||||
|
||||
assert_eq!(SPair(sym, S1), obj.name());
|
||||
assert_eq!(Some(&IdentKind::Meta), obj.kind());
|
||||
assert_eq!(Some(&src), obj.src());
|
||||
assert_eq!(Some(fragment), obj.fragment());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_fragment_to_ident_fails_if_transition_fails() -> AsgResult<()> {
|
||||
let mut sut = Sut::new();
|
||||
|
||||
let sym = "failfrag".into();
|
||||
let src = Source {
|
||||
generated: true,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
// The failure will come from terr below, not this.
|
||||
let node = sut.declare(SPair(sym, S1), IdentKind::Meta, src.clone())?;
|
||||
|
||||
// The first set will succeed.
|
||||
sut.set_fragment(SPair(sym, S2), "".into())?;
|
||||
|
||||
// This will fail.
|
||||
let result = sut.set_fragment(SPair(sym, S3), "".into());
|
||||
|
||||
// The node should have been restored.
|
||||
let obj = sut.get_ident(node).unwrap();
|
||||
|
||||
assert_eq!(SPair(sym, S1), obj.name());
|
||||
assert_matches!(result, Err(AsgError::IdentTransition(..)));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_ident_dep_to_ident() -> AsgResult<()> {
|
||||
let mut sut = Sut::new();
|
||||
|
||||
let sym = "sym".into();
|
||||
let dep = "dep".into();
|
||||
|
||||
let symnode =
|
||||
sut.declare(SPair(sym, S1), IdentKind::Meta, Source::default())?;
|
||||
let depnode =
|
||||
sut.declare(SPair(dep, S2), IdentKind::Meta, Source::default())?;
|
||||
|
||||
sut.add_dep(symnode, depnode);
|
||||
assert!(sut.has_dep(symnode, depnode));
|
||||
|
||||
// sanity check if we re-add a dep
|
||||
sut.add_dep(symnode, depnode);
|
||||
assert!(sut.has_dep(symnode, depnode));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// same as above test
|
||||
#[test]
|
||||
fn add_dep_lookup_existing() -> AsgResult<()> {
|
||||
let mut sut = Sut::new();
|
||||
|
||||
let sym = SPair("sym".into(), S1);
|
||||
let dep = SPair("dep".into(), S2);
|
||||
|
||||
let _ = sut.declare(sym, IdentKind::Meta, Source::default())?;
|
||||
let _ = sut.declare(dep, IdentKind::Meta, Source::default())?;
|
||||
|
||||
let (symnode, depnode) = sut.add_dep_lookup(sym, dep);
|
||||
assert!(sut.has_dep(symnode, depnode));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_dep_lookup_missing() -> AsgResult<()> {
|
||||
let mut sut = Sut::new();
|
||||
|
||||
let sym = SPair("sym".into(), S1);
|
||||
let dep = SPair("dep".into(), S2);
|
||||
|
||||
// both of these are missing
|
||||
let (symnode, depnode) = sut.add_dep_lookup(sym, dep);
|
||||
assert!(sut.has_dep(symnode, depnode));
|
||||
|
||||
assert_eq!(sym, sut.get_ident(symnode).unwrap().name());
|
||||
assert_eq!(dep, sut.get_ident(depnode).unwrap().name());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn declare_return_missing_symbol() -> AsgResult<()> {
|
||||
let mut sut = Sut::new();
|
||||
|
||||
let sym = SPair("sym".into(), S1);
|
||||
let dep = SPair("dep".into(), S2);
|
||||
|
||||
// both of these are missing, see add_dep_lookup_missing
|
||||
let (symnode, _) = sut.add_dep_lookup(sym, dep);
|
||||
|
||||
let src = Source {
|
||||
desc: Some("redeclare missing".into()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
// Check with a declared value
|
||||
let declared = sut.declare(sym, IdentKind::Meta, src.clone())?;
|
||||
|
||||
assert_eq!(symnode, declared);
|
||||
|
||||
let obj = sut.get_ident(declared).unwrap();
|
||||
|
||||
assert_eq!(sym, obj.name());
|
||||
assert_eq!(Some(&IdentKind::Meta), obj.kind());
|
||||
assert_eq!(Some(&src), obj.src());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn try_map_narrows_and_modifies() {
|
||||
let mut sut = Sut::new();
|
||||
|
||||
let id_a = SPair("foo".into(), S1);
|
||||
let id_b = SPair("bar".into(), S2);
|
||||
|
||||
let oi = sut.create(Ident::Missing(id_a));
|
||||
|
||||
// This is the method under test.
|
||||
// It should narrow to an `Ident` because `oi` was `create`'d with
|
||||
// an `Ident`.
|
||||
let oi_new = sut
|
||||
.try_map_obj(oi, |ident| {
|
||||
assert_eq!(ident, Ident::Missing(id_a));
|
||||
|
||||
// Replace the identifier
|
||||
Ok::<_, (_, Infallible)>(Ident::Missing(id_b))
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
// These would not typically be checked by the caller;
|
||||
// they are intended for debugging.
|
||||
assert_eq!(S1, oi.into());
|
||||
assert_eq!(S2, oi_new.into());
|
||||
|
||||
// A change in span does not change its equivalence.
|
||||
assert_eq!(oi_new, oi);
|
||||
|
||||
// Ensure that the graph was updated with the new object from the
|
||||
// above method.
|
||||
assert_eq!(&Ident::Missing(id_b), sut.get(oi).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn try_map_failure_restores_original_object() {
|
||||
let mut sut = Sut::new();
|
||||
|
||||
let id_a = SPair("foo".into(), S1);
|
||||
|
||||
let err = "uh oh";
|
||||
|
||||
let oi = sut.create(Ident::Missing(id_a));
|
||||
|
||||
// This will fail to modify the object.
|
||||
let oi_new = sut.try_map_obj(oi, |ident| {
|
||||
assert_eq!(ident, Ident::Missing(id_a));
|
||||
|
||||
Err((ident, err))
|
||||
});
|
||||
|
||||
assert_eq!(Err(err), oi_new);
|
||||
|
||||
// Ensure that the original object was retained.
|
||||
assert_eq!(&Ident::Missing(id_a), sut.get(oi).unwrap());
|
||||
}
|
Loading…
Reference in New Issue