From 1efc189e90240c162b54cbc50362b46786643dad Mon Sep 17 00:00:00 2001 From: Federico Igne Date: Wed, 18 Nov 2020 19:13:25 +0000 Subject: Reorganize project with Java-like folder structure --- .../uk/ac/ox/cs/rsacomb/CanonicalModelSpec.scala | 370 +++++++++++++++++++ .../ac/ox/cs/rsacomb/FilteringProgramSpecs.scala | 398 +++++++++++++++++++++ .../scala/uk/ac/ox/cs/rsacomb/OWLAxiomSpec.scala | 336 +++++++++++++++++ .../scala/uk/ac/ox/cs/rsacomb/OWLClassSpec.scala | 277 ++++++++++++++ 4 files changed, 1381 insertions(+) create mode 100644 src/test/scala/uk/ac/ox/cs/rsacomb/CanonicalModelSpec.scala create mode 100644 src/test/scala/uk/ac/ox/cs/rsacomb/FilteringProgramSpecs.scala create mode 100644 src/test/scala/uk/ac/ox/cs/rsacomb/OWLAxiomSpec.scala create mode 100644 src/test/scala/uk/ac/ox/cs/rsacomb/OWLClassSpec.scala (limited to 'src/test/scala/uk/ac/ox/cs') diff --git a/src/test/scala/uk/ac/ox/cs/rsacomb/CanonicalModelSpec.scala b/src/test/scala/uk/ac/ox/cs/rsacomb/CanonicalModelSpec.scala new file mode 100644 index 0000000..376729c --- /dev/null +++ b/src/test/scala/uk/ac/ox/cs/rsacomb/CanonicalModelSpec.scala @@ -0,0 +1,370 @@ +package rsacomb + +import java.io.File +import org.scalatest.LoneElement +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +import org.semanticweb.owlapi.model._ +import uk.ac.manchester.cs.owl.owlapi._ +import org.semanticweb.owlapi.dlsyntax.renderer.DLSyntaxObjectRenderer + +import tech.oxfordsemantic.jrdfox.logic.datalog.Rule +import tech.oxfordsemantic.jrdfox.logic.expression.Variable + +import scala.collection.JavaConverters._ + +import uk.ac.ox.cs.rsacomb.RSAOntology +import uk.ac.ox.cs.rsacomb.util.{RDFoxHelpers, RSA} + +object Ontology1_CanonicalModelSpec { + + /* Renderer to display OWL Axioms with DL syntax*/ + val renderer = new DLSyntaxObjectRenderer() + + def base(str: String): IRI = + IRI.create("http://example.com/rsa_example.owl#" + str) + + val ontology_path: File = new File("examples/example1.ttl") + val ontology = RSAOntology(ontology_path) + val program = ontology.canonicalModel + + val roleR = new OWLObjectPropertyImpl(base("R")) + val roleS = new OWLObjectPropertyImpl(base("S")) + val roleT = new OWLObjectPropertyImpl(base("T")) + val roleR_inv = roleR.getInverseProperty() + val roleS_inv = roleS.getInverseProperty() + val roleT_inv = roleT.getInverseProperty() + + val AsubClassOfD = new OWLSubClassOfAxiomImpl( + new OWLClassImpl(base("A")), + new OWLClassImpl(base("D")), + Seq().asJava + ) + + val DsomeValuesFromRB = new OWLSubClassOfAxiomImpl( + new OWLClassImpl(base("D")), + new OWLObjectSomeValuesFromImpl( + roleR, + new OWLClassImpl(base("B")) + ), + Seq().asJava + ) + + val BsomeValuesFromSD = new OWLSubClassOfAxiomImpl( + new OWLClassImpl(base("B")), + new OWLObjectSomeValuesFromImpl( + roleS, + new OWLClassImpl(base("D")) + ), + Seq().asJava + ) + + val AsomeValuesFromSiC = new OWLSubClassOfAxiomImpl( + new OWLClassImpl(base("A")), + new OWLObjectSomeValuesFromImpl( + roleS_inv, + new OWLClassImpl(base("C")) + ), + Seq().asJava + ) + + val SsubPropertyOfT = new OWLSubObjectPropertyOfAxiomImpl( + new OWLObjectPropertyImpl(base("S")), + new OWLObjectPropertyImpl(base("T")), + Seq().asJava + ) + +} + +class Ontology1_CanonicalModelSpec + extends AnyFlatSpec + with Matchers + with LoneElement { + + import Ontology1_CanonicalModelSpec._ + + "The program generated from Example #1" should "not be empty" in { + program.rules should not be empty + } + + renderer.render(AsubClassOfD) should "be converted into a single Rule" in { + val varX = Variable.create("X") + val visitor = program.RuleGenerator + val rules = AsubClassOfD.accept(visitor) + rules.loneElement shouldBe a[Rule] + } + + // Role R // + + renderer.render(roleR) should "be safe" in { + ontology.unsafeRoles should not contain roleR + } + + it should "have 3 elements in its conflict set" in { + ontology.confl(roleR) should have size 3 + } + + it should "contain S in its conflict set" in { + ontology.confl(roleR) should contain(roleS) + } + + it should "contain T in its conflict set" in { + ontology.confl(roleR) should contain(roleT) + } + + it should ("contain " + renderer.render( + roleR_inv + ) + " in its conflict set") in { + ontology.confl(roleR) should contain(roleR_inv) + } + + // Role S // + + renderer.render(roleS) should "be safe" in { + ontology.unsafeRoles should not contain roleS + } + + it should "have 3 elements in its conflict set" in { + ontology.confl(roleS) should have size 3 + } + + it should "contain R in its conflict set" in { + ontology.confl(roleS) should contain(roleR) + } + + it should ("contain " + renderer.render( + roleS_inv + ) + " in its conflict set") in { + ontology.confl(roleS) should contain(roleS_inv) + } + + it should ("contain " + renderer.render( + roleT_inv + ) + " in its conflict set") in { + ontology.confl(roleS) should contain(roleT_inv) + } + + // S⁻ + + renderer.render(roleS_inv) should "be unsafe" in { + ontology.unsafeRoles should contain(roleS_inv) + } + + renderer.render( + AsomeValuesFromSiC + ) should "produce 1 rule" in { + val varX = Variable.create("X") + val visitor = program.RuleGenerator + val rules = AsomeValuesFromSiC.accept(visitor) + rules should have length 1 + } + + renderer.render( + DsomeValuesFromRB + ) should "have a 'cycle' set of 48 elements" in { + // Cycle introduces a new constant for each possible triple (the + // order among triples is total). In this example there are 4 + // concept names and R has 3 safe roles in its conflict set (S, T, + // Inv(R)). Triples are + // (concept, role, concept) + // and hence we have 4*3*4=48 new constants introduced. + ontology.cycle(DsomeValuesFromRB) should have size 48 + } + + it should "produce 5 rules" in { + // Rule 1 provides 1 rule (split in 2) + 48 fact + // Rule 2 provides 0 rules + // Rule 3 provides 48 rule (split in 2) + // Then (1*2 + 48) + (0) + (48*2) = 146 + val varX = Variable.create("X") + val visitor = program.RuleGenerator + val rules = DsomeValuesFromRB.accept(visitor) + rules should have length 146 + } + + renderer.render( + BsomeValuesFromSD + ) should "have a 'cycle' set of 32 elements" in { + // Cycle introduces a new constant for each possible triple (the + // order among triples is total). In this example there are 4 + // concept names and S has 2 safe roles in its conflict set (R, + // Inv(T)). Triples are + // (concept, role, concept) + // and hence we have 4*2*4=32 new constants introduced. + ontology.cycle(BsomeValuesFromSD) should have size 32 + } + + it should "produce 5 rules" in { + // Rule 1 provides 1 rule (split in 2) + 32 fact + // Rule 2 provides 0 rules + // Rule 3 provides 32 rule (split in 2) + // Then (1*2 + 32) + (0) + (32*2) = 98 + val varX = Variable.create("X") + val visitor = program.RuleGenerator + val rules = DsomeValuesFromRB.accept(visitor) + rules should have length 146 + } + + renderer.render( + SsubPropertyOfT + ) should "produce 2 rules" in { + val varX = Variable.create("X") + val visitor = program.RuleGenerator + val rules = SsubPropertyOfT.accept(visitor) + rules should have length 2 + } + +} + +object Ontology2_CanonicalModelSpec { + + /* Renderer to display OWL Axioms with DL syntax*/ + val renderer = new DLSyntaxObjectRenderer() + + def base(str: String): IRI = + IRI.create("http://example.com/rsa_example.owl#" + str) + + val ontology_path: File = new File("examples/example2.owl") + val ontology = RSAOntology(ontology_path) + val program = ontology.canonicalModel + + val roleR = new OWLObjectPropertyImpl(base("R")) + val roleS = new OWLObjectPropertyImpl(base("S")) + val roleT = new OWLObjectPropertyImpl(base("T")) + val roleP = new OWLObjectPropertyImpl(base("P")) + val roleR_inv = roleR.getInverseProperty() + val roleS_inv = roleS.getInverseProperty() + val roleT_inv = roleT.getInverseProperty() + val roleP_inv = roleP.getInverseProperty() + + val AsomeValuesFromRB = new OWLSubClassOfAxiomImpl( + new OWLClassImpl(base("A")), + new OWLObjectSomeValuesFromImpl( + roleR, + new OWLClassImpl(base("B")) + ), + Seq().asJava + ) + + val BsomeValuesFromSC = new OWLSubClassOfAxiomImpl( + new OWLClassImpl(base("B")), + new OWLObjectSomeValuesFromImpl( + roleS, + new OWLClassImpl(base("C")) + ), + Seq().asJava + ) + + val CsomeValuesFromTD = new OWLSubClassOfAxiomImpl( + new OWLClassImpl(base("C")), + new OWLObjectSomeValuesFromImpl( + roleT, + new OWLClassImpl(base("D")) + ), + Seq().asJava + ) + + val DsomeValuesFromPA = new OWLSubClassOfAxiomImpl( + new OWLClassImpl(base("D")), + new OWLObjectSomeValuesFromImpl( + roleP, + new OWLClassImpl(base("A")) + ), + Seq().asJava + ) + +} + +class Ontology2_CanonicalModelSpec + extends AnyFlatSpec + with Matchers + with LoneElement { + + import Ontology2_CanonicalModelSpec._ + + "The program generated from Example #1" should "not be empty" in { + program.rules should not be empty + } + + // Role R // + + renderer.render(roleR) should "be unsafe" in { + ontology.unsafeRoles should contain(roleR) + } + + it should "have only its inverse in its conflict set" in { + ontology.confl(roleR).loneElement shouldBe roleR_inv + } + + // Role S // + + renderer.render(roleS) should "be unsafe" in { + ontology.unsafeRoles should contain(roleS) + } + + it should "have only its inverse in its conflict set" in { + ontology.confl(roleS).loneElement shouldBe roleS_inv + } + + // Role T // + + renderer.render(roleT) should "be unsafe" in { + ontology.unsafeRoles should contain(roleT) + } + + it should "have only its inverse in its conflict set" in { + ontology.confl(roleT).loneElement shouldBe roleT_inv + } + + // Role P // + + renderer.render(roleP) should "be unsafe" in { + ontology.unsafeRoles should contain(roleP) + } + + it should "have only its inverse in its conflict set" in { + ontology.confl(roleP).loneElement shouldBe roleP_inv + } + + // A ⊑ ∃ R.B + + renderer.render( + AsomeValuesFromRB + ) should "produce 1 rule" in { + val visitor = program.RuleGenerator + val rules = AsomeValuesFromRB.accept(visitor) + rules should have length 1 + } + + // B ⊑ ∃ S.C + + renderer.render( + BsomeValuesFromSC + ) should "produce 1 rule" in { + val visitor = program.RuleGenerator + val rules = BsomeValuesFromSC.accept(visitor) + rules should have length 1 + } + + // C ⊑ ∃ T.D + + renderer.render( + CsomeValuesFromTD + ) should "produce 1 rule" in { + val visitor = program.RuleGenerator + val rules = CsomeValuesFromTD.accept(visitor) + rules should have length 1 + } + + // D ⊑ ∃ P.A + + renderer.render( + DsomeValuesFromPA + ) should "produce 1 rule" in { + val visitor = program.RuleGenerator + val rules = DsomeValuesFromPA.accept(visitor) + rules should have length 1 + } + +} diff --git a/src/test/scala/uk/ac/ox/cs/rsacomb/FilteringProgramSpecs.scala b/src/test/scala/uk/ac/ox/cs/rsacomb/FilteringProgramSpecs.scala new file mode 100644 index 0000000..49abd48 --- /dev/null +++ b/src/test/scala/uk/ac/ox/cs/rsacomb/FilteringProgramSpecs.scala @@ -0,0 +1,398 @@ +package rsacomb + +import java.io.File +import java.util.{ArrayList => JList} +import org.scalatest.LoneElement +import org.scalatest.Inspectors +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +import tech.oxfordsemantic.jrdfox.logic.datalog.TupleTableAtom +import tech.oxfordsemantic.jrdfox.logic.expression.{Variable, IRI} +import tech.oxfordsemantic.jrdfox.logic.sparql.statement.{Query, SelectQuery} +import tech.oxfordsemantic.jrdfox.Prefixes + +import scala.collection.JavaConverters._ + +import uk.ac.ox.cs.rsacomb.FilteringProgram +import uk.ac.ox.cs.rsacomb.util.RDFoxHelpers + +object FilteringProgramSpec { + + val prefixes = new Prefixes() + prefixes.declarePrefix( + ":", + "http://slegger.gitlab.io/slegge-obda/ontology/subsurface-exploration#" + ) + prefixes.declarePrefix("rdf:", "http://www.w3.org/1999/02/22-rdf-syntax-ns#") + prefixes.declarePrefix("rdfs:", "http://www.w3.org/2000/01/rdf-schema#") + prefixes.declarePrefix("owl:", "http://www.w3.org/2002/07/owl#") + + // DEBUG: Quick helper functions + def v(v: String): Variable = Variable.create(v) + def c(c: String): IRI = IRI.create(":" + c) + + // QUERY 0 + + val query0 = RDFoxHelpers + .parseSelectQuery(""" + SELECT ?subj + WHERE { + ?subj ?pred ?obj + } + """, prefixes) + .get + + // val query0 = Query.create( + // QueryType.SELECT, + // false, + // List(v("subj")).asJava, + // Atom.rdf(v("subj"), v("pred"), v("obj")) + // ) + + // QUERY 1 + + val query1 = RDFoxHelpers + .parseSelectQuery(""" + SELECT * + WHERE { + ?w a :Wellbore + } + """, prefixes) + .get + + // val query1 = Query.create( + // QueryType.SELECT, + // false, + // List(v("w")).asJava, + // Atom.rdf(v("w"), IRI.RDF_TYPE, c("Wellbore")) + // ) + + // QUERY 2 + + val query2 = RDFoxHelpers + .parseSelectQuery( + """ + SELECT * + WHERE { + ?w a :Wellbore ; + :wellboreDocument ?doc . + ?doc :hasURL ?document_hyperlink + } + """, + prefixes + ) + .get + + // val query2 = Query.create( + // QueryType.SELECT, + // false, + // List(v("w"), v("doc"), v("document_hyperlink")).asJava, + // Conjunction.create( + // Atom.rdf(v("w"), IRI.RDF_TYPE, c("Wellbore")), + // Atom.rdf(v("w"), c("wellboreDocument"), v("doc")), + // Atom.rdf(v("doc"), c("hasURL"), v("document_hyperlink")) + // ) + // ) + + // QUERY 3 + + val query3 = RDFoxHelpers + .parseSelectQuery( + """ + SELECT ?wellbore ?formation_pressure + WHERE { + ?w a :Wellbore ; + :name ?wellbore ; + :hasFormationPressure ?fp . + ?fp :valueInStandardUnit ?formation_pressure + } + """, + prefixes + ) + .get + + // val query3 = Query.create( + // QueryType.SELECT, + // false, + // List(v("wellbore"), v("formation_pressure")).asJava, + // Conjunction.create( + // Atom.rdf(v("w"), IRI.RDF_TYPE, c("Wellbore")), + // Atom.rdf(v("w"), c("name"), v("wellbore")), + // Atom.rdf(v("w"), c("hasFormationPressure"), v("fp")), + // Atom.rdf(v("fp"), c("valueInStandardUnit"), v("formation_pressure")) + // ) + // ) + + // QUERY 4 + + val query4 = RDFoxHelpers + .parseSelectQuery( + """ + SELECT * + WHERE { + ?w a :Wellbore ; + :hasGeochemicalMeasurement ?measurement . + ?measurement :cgType ?cgtype ; + :peakName ?peakType ; + :peakHeight ?peak_height ; + :peakAmount ?peak_amount + } + """, + prefixes + ) + .get + + // val query4 = Query.create( + // QueryType.SELECT, + // false, + // List( + // v("w"), + // v("measurement"), + // v("cgtype"), + // v("peakType"), + // v("peak_height"), + // v("peak_amount") + // ).asJava, + // Conjunction.create( + // Atom.rdf(v("w"), IRI.RDF_TYPE, c("Wellbore")), + // Atom.rdf(v("w"), c("hasGeochemicalMeasurement"), v("measurement")), + // Atom.rdf(v("measurement"), c("cgType"), v("cgtype")), + // Atom.rdf(v("measurement"), c("peakName"), v("peakType")), + // Atom.rdf(v("measurement"), c("peakHeight"), v("peak_height")), + // Atom.rdf(v("measurement"), c("peakAmount"), v("peak_amount")) + // ) + // ) + + // QUERY 5 + + val query5 = RDFoxHelpers + .parseSelectQuery( + """ + SELECT ?wellbore ?unit_name ?discovery + WHERE { + ?w a :Wellbore ; + :name ?wellbore ; + :hasWellboreInterval ?c_int ; + :hasWellboreInterval ?f_int . + ?c_int :hasUnit ?c_unit . + ?c_unit :name ?unit_name . + ?f_int a :FluidZone ; + :name ?discovery ; + :overlapsWellboreInterval ?c_int + } + """, + prefixes + ) + .get + + // val query5 = Query.create( + // QueryType.SELECT, + // false, + // List(v("wellbore"), v("unit_name"), v("discovery")).asJava, + // Conjunction.create( + // Atom.rdf(v("w"), IRI.RDF_TYPE, c("Wellbore")), + // Atom.rdf(v("w"), c("name"), v("wellbore")), + // Atom.rdf(v("w"), c("hasWellboreInterval"), v("c_int")), + // Atom.rdf(v("w"), c("hasWellboreInterval"), v("f_int")), + // Atom.rdf(v("c_int"), c("hasUnit"), v("c_unit")), + // Atom.rdf(v("c_unit"), c("name"), v("unit_name")), + // Atom.rdf(v("f_int"), IRI.RDF_TYPE, c("FluidZone")), + // Atom.rdf(v("f_int"), c("name"), v("discovery")), + // Atom.rdf(v("f_int"), c("overlapsWellboreInterval"), v("c_int")) + // ) + // ) + + // QUERY 6 + + val query6 = RDFoxHelpers + .parseSelectQuery( + """ + SELECT DISTINCT ?wellbore ?content + WHERE { + ?w a :Wellbore ; + :name ?wellbore ; + :hasWellboreInterval ?int . + ?int a :FluidZone ; + :fluidZoneContent ?content + } + """, + prefixes + ) + .get + + // val query6 = Query.create( + // QueryType.SELECT, + // true, + // List(v("wellbore"), v("content")).asJava, + // Conjunction.create( + // Atom.rdf(v("w"), IRI.RDF_TYPE, c("Wellbore")), + // Atom.rdf(v("w"), c("name"), v("wellbore")), + // Atom.rdf(v("w"), c("hasWellboreInterval"), v("int")), + // Atom.rdf(v("int"), IRI.RDF_TYPE, c("FluidZone")), + // Atom.rdf(v("int"), c("fluidZoneContent"), v("content")) + // ) + // ) + + // QUERY 7 + + val query7 = RDFoxHelpers + .parseSelectQuery( + """ + SELECT ?wName ?sample ?porosity ?top_depth_md ?bot_depth_md + WHERE { + ?w a :Wellbore ; + :name ?wName ; + :hasWellboreInterval ?z . + ?z :hasUnit ?u . + ?u :name ?strat_unit_name . + ?wellbore :hasWellboreInterval ?cored_int . + ?c :extractedFrom ?cored_int ; + :hasCoreSample ?sample . + ?sample :hasDepth ?sample_depth . + ?sample_depth + :inWellboreInterval ?z . + ?sample :hasPorosity ?p . + ?p :valueInStandardUnit ?porosity . + ?z :hasTopDepth ?top . + ?top a :MeasuredDepth ; + :valueInStandardUnit ?top_depth_md . + ?z :hasBottomDepth ?bot . + ?bot a :MeasuredDepth ; + :valueInStandardUnit ?bot_depth_md + } + """, + prefixes + ) + .get + + // val query7 = Query.create( + // QueryType.SELECT, + // false, + // List( + // v("wName"), + // v("sample"), + // v("porosity"), + // v("top_depth_md"), + // v("bot_depth_md") + // ).asJava, + // Conjunction.create( + // Atom.rdf(v("w"), IRI.RDF_TYPE, c("Wellbore")), + // Atom.rdf(v("w"), c("name"), v("wName")), + // Atom.rdf(v("w"), c("hasWellboreInterval"), v("z")), + // Atom.rdf(v("z"), c("hasUnit"), v("u")), + // Atom.rdf(v("u"), c("name"), v("strat_unit_name")), + // Atom.rdf(v("wellbore"), c("hasWellboreInterval"), v("cored_int")), + // Atom.rdf(v("c"), c("extractedFrom"), v("cored_int")), + // Atom.rdf(v("c"), c("hasCoreSample"), v("sample")), + // Atom.rdf(v("sample"), c("hasDepth"), v("sample_depth")), + // Atom.rdf(v("sample_depth"), c("inWellboreInterval"), v("z")), + // Atom.rdf(v("sample"), c("hasPorosity"), v("p")), + // Atom.rdf(v("p"), c("valueInStandardUnit"), v("porosity")), + // Atom.rdf(v("z"), c("hasTopDepth"), v("top")), + // Atom.rdf(v("top"), IRI.RDF_TYPE, c("MeasuredDepth")), + // Atom.rdf(v("top"), c("valueInStandardUnit"), v("top_depth_md")), + // Atom.rdf(v("z"), c("hasBottomDepth"), v("bot")), + // Atom.rdf(v("bot"), IRI.RDF_TYPE, c("MeasuredDepth")), + // Atom.rdf(v("bot"), c("valueInStandardUnit"), v("bot_depth_md")) + // ) + // ) + + val queries = + List(query0, query1, query2, query3, query4, query5, query6, query7) +} + +class FilteringProgramSpec + extends AnyFlatSpec + with Matchers + with LoneElement + with Inspectors { + + import FilteringProgramSpec._ + + "Queries" should "have distinct answer and bounded variables" in { + for (query <- queries) { + val program = new FilteringProgram(query, List()) + forAll(program.answer) { v => program.bounded should not contain v } + forAll(program.bounded) { v => program.answer should not contain v } + } + } + + "Query 0" should "have {?obj, ?pred} as bounded variables" in { + val pred = Variable.create("obj") + val obj = Variable.create("pred") + val program = new FilteringProgram(query0, List()) + program.bounded should contain theSameElementsAs List(pred, obj) + } + + "Query 1" should "have no bounded variable" in { + val program = new FilteringProgram(query1, List()) + program.bounded shouldBe empty + } + + "Query 2" should "have no bounded variable" in { + val program = new FilteringProgram(query2, List()) + program.bounded shouldBe empty + } + + "Query 3" should "have {?w, ?fp} as bounded variables" in { + val w = Variable.create("w") + val fp = Variable.create("fp") + val program = new FilteringProgram(query3, List()) + program.bounded should contain theSameElementsAs List(w, fp) + } + + "Query 4" should "have no bounded variable" in { + val program = new FilteringProgram(query4, List()) + program.bounded shouldBe empty + } + + "Query 5" should "have a non-empty bounded set" in { + val w = Variable.create("w") + val c_int = Variable.create("c_int") + val f_int = Variable.create("f_int") + val c_unit = Variable.create("c_unit") + val program = new FilteringProgram(query5, List()) + program.bounded should contain theSameElementsAs List( + w, + c_int, + f_int, + c_unit + ) + } + + "Query 6" should "have a non-empty bounded set" in { + val w = Variable.create("w") + val int = Variable.create("int") + val program = new FilteringProgram(query6, List()) + program.bounded should contain theSameElementsAs List(w, int) + } + + "Query 7" should "have a non-empty bounded set" in { + val w = Variable.create("w") + val z = Variable.create("z") + val u = Variable.create("u") + val strat_unit_name = Variable.create("strat_unit_name") + val wellbore = Variable.create("wellbore") + val cored_int = Variable.create("cored_int") + val c = Variable.create("c") + val sample_depth = Variable.create("sample_depth") + val p = Variable.create("p") + val top = Variable.create("top") + val bot = Variable.create("bot") + val program = new FilteringProgram(query7, List()) + program.bounded should contain theSameElementsAs List( + w, + z, + u, + strat_unit_name, + wellbore, + cored_int, + c, + sample_depth, + p, + top, + bot + ) + } +} diff --git a/src/test/scala/uk/ac/ox/cs/rsacomb/OWLAxiomSpec.scala b/src/test/scala/uk/ac/ox/cs/rsacomb/OWLAxiomSpec.scala new file mode 100644 index 0000000..8aee03d --- /dev/null +++ b/src/test/scala/uk/ac/ox/cs/rsacomb/OWLAxiomSpec.scala @@ -0,0 +1,336 @@ +package rsacomb + +import java.util.{ArrayList => JList} +import org.scalatest.LoneElement +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +import org.semanticweb.owlapi.model.OWLClassExpression +import uk.ac.manchester.cs.owl.owlapi.{OWLSubClassOfAxiomImpl} +import uk.ac.manchester.cs.owl.owlapi.{ + OWLClassImpl, + OWLObjectSomeValuesFromImpl, + OWLObjectIntersectionOfImpl, + OWLObjectOneOfImpl, + OWLObjectAllValuesFromImpl, + OWLObjectMaxCardinalityImpl, + OWLNamedIndividualImpl +} +import uk.ac.manchester.cs.owl.owlapi.{OWLObjectPropertyImpl} +import org.semanticweb.owlapi.model.{OWLAxiom} + +import tech.oxfordsemantic.jrdfox.logic.Datatype +import tech.oxfordsemantic.jrdfox.logic.datalog.{ + Rule, + BindAtom, + TupleTableAtom, + TupleTableName +} +import tech.oxfordsemantic.jrdfox.logic.expression.{ + FunctionCall, + Term, + Variable, + Literal +} + +import org.semanticweb.owlapi.model.{IRI => OWLIRI} +import tech.oxfordsemantic.jrdfox.logic.expression.{IRI => RDFIRI} + +import uk.ac.ox.cs.rsacomb.converter.{RDFoxAxiomConverter, SkolemStrategy} +import uk.ac.ox.cs.rsacomb.util.RSA + +object OWLAxiomSpec { + + // IRI + val iri_Professor = OWLIRI.create("univ:Professor") + val iri_Female = OWLIRI.create("std:Female") + val iri_Student = OWLIRI.create("univ:Student") + val iri_PartTimeStudent = OWLIRI.create("univ:PartTimeStudent") + val iri_Worker = OWLIRI.create("univ:Worker") + val iri_alice = OWLIRI.create("univ:alice") + val iri_supervises = OWLIRI.create("univ:supervises") + val iri_hasSupervisor = OWLIRI.create("univ:hasSupervisor") + val iri_sameAs = OWLIRI.create("owl:sameAs") + + // RDFox Terms + val term_x = Variable.create("x") + val term_y = Variable.create("y") + val term_z = Variable.create("z") + val term_c1 = RSA("c_1") + val term_c2 = RSA("c_2") + val term_alice = RDFIRI.create("univ:alice") + + // RDFox Predicates + val pred_sameAs = TupleTableName.create("owl:sameAs") + val pred_Professor = TupleTableName.create(iri_Professor.getIRIString) + val pred_hasSupervisor = TupleTableName.create(iri_hasSupervisor.getIRIString) + + // OWL Classes + // Name Class corresponding to + // + // Professor + // + val class_Professor = new OWLClassImpl(iri_Professor) + val class_Female = new OWLClassImpl(iri_Female) + val class_Student = new OWLClassImpl(iri_Student) + val class_PartTimeStudent = new OWLClassImpl(iri_PartTimeStudent) + val class_Worker = new OWLClassImpl(iri_Worker) + val class_OWLClass = class_Professor + // Class Conjunction corresponding to + // + // Student ∧ Worker + // + val class_OWLObjectIntersectionOf = { + val conjuncts = new JList[OWLClassExpression]() + conjuncts.add(class_Student) + conjuncts.add(class_Worker) + new OWLObjectIntersectionOfImpl(conjuncts) + } + // Singleton Class corresponding to + // + // { alice } + // + val class_OWLObjectOneOf = + new OWLObjectOneOfImpl( + new OWLNamedIndividualImpl(iri_alice) + ) + // Object Existential Restiction corresponding to + // + // ∃ hasSupervisor.Professor + // + val class_OWLObjectSomeValuesFrom = + new OWLObjectSomeValuesFromImpl( + new OWLObjectPropertyImpl(iri_hasSupervisor), + class_Professor + ) + // Object Max Cardinality Restriction corresponding to + // + // ≤ 1 hasSupervisor . Professor + val class_OWLObjectMaxCardinality = + new OWLObjectMaxCardinalityImpl( + new OWLObjectPropertyImpl(iri_hasSupervisor), + 1, + class_Professor + ) + + // OWL Axioms + // Axiom SubClassOf corresponding to + // + // Student ∧ Worker -> PartTimeStudent + // + val axiom_OWLSubClassOf1 = + new OWLSubClassOfAxiomImpl( + class_OWLObjectIntersectionOf, + class_PartTimeStudent, + new JList() + ) + + // Axiom SubClassOf corresponding to + // + // Student -> ∃ hasSupervisor.Professor + // + val axiom_OWLSubClassOf2 = + new OWLSubClassOfAxiomImpl( + class_Student, + class_OWLObjectSomeValuesFrom, + new JList() + ) + + // Axiom SubClassOf corresponding to + // + // ∃ hasSupervisor.Professor -> Student + // + val axiom_OWLSubClassOf3 = + new OWLSubClassOfAxiomImpl( + class_OWLObjectSomeValuesFrom, + class_Student, + new JList() + ) + + // Axiom SubClassOf corresponding to + // + // Student -> { alice } + // + val axiom_OWLSubClassOf4 = + new OWLSubClassOfAxiomImpl( + class_Student, + class_OWLObjectOneOf, + new JList() + ) + + // Axiom SubClassOf corresponding to + // + // Student -> ≤1 hasSupervisor.Professor + // + val axiom_OWLSubClassOf5 = + new OWLSubClassOfAxiomImpl( + class_Student, + class_OWLObjectMaxCardinality, + new JList() + ) + + def convertAxiom( + axiom: OWLAxiom, + term: Term, + skolem: SkolemStrategy = SkolemStrategy.None + ): List[Rule] = { + axiom.accept(RDFoxAxiomConverter(term, List())) + } + +} // object OWLAxiomSpec + +class OWLAxiomSpec extends AnyFlatSpec with Matchers with LoneElement { + + // Import required data + import OWLAxiomSpec._ + // Implicit convertion from IRI in OWLAPI to IRI in JRDFox + import uk.ac.ox.cs.rsacomb.implicits.RDFox._ + + // OWLSubClassOfAxiom #1 + axiom_OWLSubClassOf1.toString should "be converted into a singleton List[Rule]" in { + val result = convertAxiom(axiom_OWLSubClassOf1, term_x) + result.loneElement shouldBe a[Rule] + } + + it should "contain a conjuction of atoms (Student[?x],Worker[?x]) in the body of the rule" in { + val result = convertAxiom(axiom_OWLSubClassOf1, term_x) + val body = List( + TupleTableAtom.rdf(term_x, RDFIRI.RDF_TYPE, iri_Student), + TupleTableAtom.rdf(term_x, RDFIRI.RDF_TYPE, iri_Worker) + ) + result.loneElement.getBody should contain theSameElementsAs body + } + + it should "contain a single atom (PartTimeStudent[?x]) in the head of the rule" in { + val result = convertAxiom(axiom_OWLSubClassOf1, term_x) + val head = TupleTableAtom.rdf(term_x, RDFIRI.RDF_TYPE, iri_PartTimeStudent) + result.loneElement.getHead.loneElement should be(head) + } + + // OWLSubClassOfAxiom #2 (w/ constant skolemization) + (axiom_OWLSubClassOf2.toString + "\n(w/ constant skolemization)") should + "be converted into a singleton List[Rule]" in { + val skolem = SkolemStrategy.Constant(axiom_OWLSubClassOf2.toString) + val result = convertAxiom(axiom_OWLSubClassOf2, term_x, skolem) + result.loneElement shouldBe a[Rule] + } + + it should "contain a single atom (Student[?x]) in the body of the rule" in { + val skolem = SkolemStrategy.Constant(axiom_OWLSubClassOf2.toString) + val result = convertAxiom(axiom_OWLSubClassOf2, term_x, skolem) + val body = + TupleTableAtom.rdf(term_x, RDFIRI.RDF_TYPE, iri_Student.getIRIString) + result.loneElement.getBody.loneElement should equal(body) + } + + // it should "contain a conjuction of atoms (hasSupervisor[?x,?c],Professor[?c]) in the head of the rule" in { + // val skolem = SkolemStrategy.Constant(axiom_OWLSubClassOf2.toString) + // val result = convertAxiom(axiom_OWLSubClassOf2, term_x, skolem) + // val term_c = RSA.rsa(skolem.const.getIRI) + // val head = List( + // TupleTableAtom.rdf(term_x, iri_hasSupervisor, term_c), + // TupleTableAtom.rdf(term_c, RDFIRI.RDF_TYPE, iri_Professor) + // ) + // result.loneElement.getHead should contain theSameElementsAs (head) + // } + + // OWLSubClassOfAxiom #2 (w/ skolemization) + (axiom_OWLSubClassOf2.toString + "\n(w/ skolemization)") should + "be converted into a singleton List[Rule]" in { + val skolem = SkolemStrategy.Standard(axiom_OWLSubClassOf2.toString) + val result = convertAxiom(axiom_OWLSubClassOf2, term_x, skolem) + result.loneElement shouldBe a[Rule] + } + + it should "contain an atom (Student[?x]) in the body of the rule" in { + val skolem = SkolemStrategy.Standard(axiom_OWLSubClassOf2.toString) + val result = convertAxiom(axiom_OWLSubClassOf2, term_x, skolem) + val body = + TupleTableAtom.rdf(term_x, RDFIRI.RDF_TYPE, iri_Student) + result.loneElement.getBody should contain(body) + } + + // it should "contain a built-in function call (BIND(?y,SKOLEM(?f,?x))) in the body of the rule" in { + // val skolem = SkolemStrategy.Standard(axiom_OWLSubClassOf2.toString) + // val result = convertAxiom(axiom_OWLSubClassOf2, term_x, skolem) + // val call = + // BindAtom.create(BuiltinFunctionCall.create("SKOLEM", term_x), term_y) + // result.loneElement.getBody should contain(call) + // } + + // it should "contain a conjuction of atom (hasSupervisor[?x,?y],Professor[?y]) in the head of the rule" in { + // val skolem = SkolemStrategy.Standard(axiom_OWLSubClassOf2.toString) + // val result = convertAxiom(axiom_OWLSubClassOf2, term_x, skolem) + // val head = List( + // TupleTableAtom.rdf(term_x, iri_hasSupervisor, term_y), + // TupleTableAtom.rdf(term_y, RDFIRI.RDF_TYPE, iri_Professor) + // ) + // result.loneElement.getHead should contain theSameElementsAs head + // } + + // OWLSubClassOfAxiom #3 + axiom_OWLSubClassOf3.toString should "be converted into a singleton List[Rule]" in { + val result = convertAxiom(axiom_OWLSubClassOf3, term_x) + result.loneElement shouldBe a[Rule] + } + + // it should "contain a conjunction of atoms (hasSupervisor[?x,?y],Professor[?y]) in the body of the rule" in { + // val result = convertAxiom(axiom_OWLSubClassOf3, term_x) + // val body = List( + // TupleTableAtom.rdf(term_x, iri_hasSupervisor, term_y), + // TupleTableAtom.rdf(term_y, RDFIRI.RDF_TYPE, iri_Professor) + // ) + // result.loneElement.getBody should contain theSameElementsAs body + // } + + it should "contain a single atom (Student[?x]) in the head of the rule" in { + val result = convertAxiom(axiom_OWLSubClassOf3, term_x) + val head = + TupleTableAtom.rdf(term_x, RDFIRI.RDF_TYPE, iri_Student) + result.loneElement.getHead.loneElement should be(head) + } + + // OWLSubClassOfAxiom #4 + axiom_OWLSubClassOf4.toString should "be converted into a singleton List[Rule]" in { + val result = convertAxiom(axiom_OWLSubClassOf4, term_x) + result.loneElement shouldBe a[Rule] + } + + it should "contain a single atoms (Student[?x]) in the body of the rule" in { + val result = convertAxiom(axiom_OWLSubClassOf4, term_x) + val body = + TupleTableAtom.rdf(term_x, RDFIRI.RDF_TYPE, iri_Student) + result.loneElement.getBody.loneElement should be(body) + } + + it should "contain a single atom (sameAs[?x,alice])) in the head of the rule" in { + val result = convertAxiom(axiom_OWLSubClassOf4, term_x) + val head = TupleTableAtom.rdf(term_x, RDFIRI.SAME_AS, term_alice) + result.loneElement.getHead.loneElement should be(head) + } + + // OWLSubClassOfAxiom #5 + axiom_OWLSubClassOf5.toString should "be converted into a singleton List[Rule]" in { + val result = convertAxiom(axiom_OWLSubClassOf5, term_x) + result.loneElement shouldBe a[Rule] + } + + // it should "contain a conjunction of atoms (...) in the body of the rule" in { + // val result = convertAxiom(axiom_OWLSubClassOf5, term_x) + // val body = List( + // TupleTableAtom.rdf(term_x, RDFIRI.RDF_TYPE, iri_Student), + // TupleTableAtom.rdf(term_x, iri_hasSupervisor, term_y), + // TupleTableAtom.rdf(term_y, RDFIRI.RDF_TYPE, iri_Professor), + // TupleTableAtom.rdf(term_x, iri_hasSupervisor, term_z), + // TupleTableAtom.rdf(term_z, RDFIRI.RDF_TYPE, iri_Professor) + // ) + // result.loneElement.getBody should contain theSameElementsAs body + // } + + // it should "contain a single atom (sameAs[?x,?z])) in the head of the rule" in { + // val result = convertAxiom(axiom_OWLSubClassOf5, term_x) + // val head = TupleTableAtom.rdf(term_y, RDFIRI.SAME_AS, term_z) + // result.loneElement.getHead.loneElement should be(head) + // } + +} // class OWLAxiomSpec diff --git a/src/test/scala/uk/ac/ox/cs/rsacomb/OWLClassSpec.scala b/src/test/scala/uk/ac/ox/cs/rsacomb/OWLClassSpec.scala new file mode 100644 index 0000000..459fe21 --- /dev/null +++ b/src/test/scala/uk/ac/ox/cs/rsacomb/OWLClassSpec.scala @@ -0,0 +1,277 @@ +package rsacomb + +import java.util.{ArrayList => JList} + +import org.scalatest.LoneElement +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +import org.semanticweb.owlapi.model.OWLClassExpression +import uk.ac.manchester.cs.owl.owlapi.{ + OWLClassImpl, + OWLObjectSomeValuesFromImpl, + OWLObjectIntersectionOfImpl, + OWLObjectOneOfImpl, + OWLObjectAllValuesFromImpl, + OWLObjectMaxCardinalityImpl, + OWLNamedIndividualImpl +} +import uk.ac.manchester.cs.owl.owlapi.{OWLObjectPropertyImpl} +import org.semanticweb.owlapi.model.IRI +import tech.oxfordsemantic.jrdfox.logic.expression.{IRI => RDFIRI} + +import tech.oxfordsemantic.jrdfox.logic.Datatype +import tech.oxfordsemantic.jrdfox.logic.datalog.{ + TupleTableAtom, + TupleTableName, + BindAtom +} +import tech.oxfordsemantic.jrdfox.logic.expression.{ + FunctionCall, + Term, + Variable, + Literal +} + +import uk.ac.ox.cs.rsacomb.converter.{ + RDFoxRuleShards, + RDFoxClassExprConverter, + SkolemStrategy +} +import uk.ac.ox.cs.rsacomb.util.RSA + +object OWLClassSpec { + + // IRI + val iri_Professor = IRI.create("univ:Professor") + val iri_Female = IRI.create("std:Female") + val iri_Student = IRI.create("univ:Student") + val iri_Worker = IRI.create("univ:Worker") + val iri_alice = IRI.create("univ:alice") + val iri_supervises = IRI.create("univ:supervises") + val iri_hasSupervisor = IRI.create("univ:hasSupervisor") + + // RDFox Terms + val term_x = Variable.create("x") + val term_y = Variable.create("y") + val term_c1 = RSA("c_1") + val term_c2 = RSA("c_2") + val term_alice = RDFIRI.create("univ:alice") + + // RDFox Predicates + val pred_sameAs = TupleTableName.create("owl:sameAs") + val pred_Professor = TupleTableName.create(iri_Professor.getIRIString) + val pred_hasSupervisor = TupleTableName.create(iri_hasSupervisor.getIRIString) + + // OWL Classes + // Name Class corresponding to + // + // Professor + // + val class_Professor = new OWLClassImpl(iri_Professor) + val class_Female = new OWLClassImpl(iri_Female) + val class_Student = new OWLClassImpl(iri_Student) + val class_Worker = new OWLClassImpl(iri_Worker) + val class_OWLClass = class_Professor + + // Class Conjunction corresponding to + // + // Female ∧ Student ∧ Worker + // + val class_OWLObjectIntersectionOf = { + val conjuncts = new JList[OWLClassExpression]() + conjuncts.add(class_Female) + conjuncts.add(class_Student) + conjuncts.add(class_Worker) + new OWLObjectIntersectionOfImpl(conjuncts) + } + // Singleton Class corresponding to + // + // { alice } + // + val class_OWLObjectOneOf = + new OWLObjectOneOfImpl( + new OWLNamedIndividualImpl(iri_alice) + ) + // Object Existential Restiction corresponding to + // + // ∃ hasSupervisor.Professor + // + val class_OWLObjectSomeValuesFrom = + new OWLObjectSomeValuesFromImpl( + new OWLObjectPropertyImpl(iri_hasSupervisor), + class_Professor + ) + // Object Max Cardinality Restriction corresponding to + // + // ≤1 hasSupervisor.Professor + val class_OWLObjectMaxCardinality = + new OWLObjectMaxCardinalityImpl( + new OWLObjectPropertyImpl(iri_hasSupervisor), + 1, + class_Professor + ) +} // object OWLClassSpec + +class OWLClassSpec extends AnyFlatSpec with Matchers with LoneElement { + // Import required data + import OWLClassSpec._ + + // OWLClass + class_OWLClass.toString should "be converted into a RDFoxRuleShards" in { + val visitor = RDFoxClassExprConverter(term_x) + val result = class_OWLClass.accept(visitor) + result shouldBe a[RDFoxRuleShards] + } + + it should "have a single TupleTableAtom in its result list" in { + val visitor = RDFoxClassExprConverter(term_x) + val result = class_OWLClass.accept(visitor) + result.res.loneElement shouldBe an[TupleTableAtom] + } + + it should "have an empty extension list" in { + val visitor = RDFoxClassExprConverter(term_x) + val result = class_OWLClass.accept(visitor) + result.ext shouldBe empty + } + + // OWLObjectIntersectionOf + class_OWLObjectIntersectionOf.toString should "be converted into a RDFoxRuleShards" in { + val visitor = RDFoxClassExprConverter(term_x) + val result = class_OWLObjectIntersectionOf.accept(visitor) + result shouldBe a[RDFoxRuleShards] + } + + it should "be converted in the union of its converted conjuncts" in { + val visitor = RDFoxClassExprConverter(term_x) + val result1 = class_OWLObjectIntersectionOf.accept(visitor) + val result2 = RDFoxClassExprConverter.merge( + List( + class_Female.accept(visitor), + class_Student.accept(visitor), + class_Worker.accept(visitor) + ) + ) + result1.res should contain theSameElementsAs result2.res + result1.ext should contain theSameElementsAs result2.ext + } + + // OWLObjectOneOf + class_OWLObjectOneOf.toString should "be converted into a RDFoxRuleShards" in { + val visitor = RDFoxClassExprConverter(term_x) + val result = class_OWLObjectOneOf.accept(visitor) + result shouldBe a[RDFoxRuleShards] + } + + // it should "be converted into a single TupleTableAtom" in { + // val visitor = RDFoxClassExprConverter(term_x) + // val result = class_OWLObjectOneOf.accept(visitor) + // result.res.loneElement should (be (a [TupleTableAtom]) and have ('tupleTableName (pred_sameAs))) + // } + + it should "have an empty extension list" in { + val visitor = RDFoxClassExprConverter(term_x) + val result = class_OWLObjectOneOf.accept(visitor) + result.ext shouldBe empty + } + + // OWLObjectSomeValuesFrom + (class_OWLObjectSomeValuesFrom.toString ++ " w/o skolemization") should + "be converted into a RDFoxRuleShards" in { + val visitor = RDFoxClassExprConverter(term_x) + val result = class_OWLObjectSomeValuesFrom.accept(visitor) + result shouldBe a[RDFoxRuleShards] + } + + it should "have two TupleTableAtoms in its result list" in { + val visitor = RDFoxClassExprConverter(term_x) + val result = class_OWLObjectSomeValuesFrom.accept(visitor) + exactly(2, result.res) should (be(an[TupleTableAtom]) + //and have('numberOfArguments (3)) + ) + } + + it should "have an empty extension list" in { + val visitor = RDFoxClassExprConverter(term_x) + val result = class_OWLObjectSomeValuesFrom.accept(visitor) + result.ext shouldBe empty + } + + (class_OWLObjectSomeValuesFrom.toString ++ " w/ skolemization") should + "be converted into a RDFoxRuleShards" in { + val skolem = SkolemStrategy.Standard(class_OWLObjectSomeValuesFrom.toString) + val visitor = RDFoxClassExprConverter(term_x, List(), skolem) + val result = class_OWLObjectSomeValuesFrom.accept(visitor) + result shouldBe a[RDFoxRuleShards] + } + + it should "have exactly two TupleTableAtoms in its result list" in { + val skolem = SkolemStrategy.Standard(class_OWLObjectSomeValuesFrom.toString) + val visitor = RDFoxClassExprConverter(term_x, List(), skolem) + val result = class_OWLObjectSomeValuesFrom.accept(visitor) + exactly(2, result.res) should (be(an[TupleTableAtom]) + //and have('numberOfArguments (3)) + ) + } + + it should "should have a single SKOLEM call in the extension list" in { + val skolem = SkolemStrategy.Standard(class_OWLObjectSomeValuesFrom.toString) + val visitor = RDFoxClassExprConverter(term_x, List(), skolem) + val result = class_OWLObjectSomeValuesFrom.accept(visitor) + result.ext.loneElement shouldBe a[BindAtom] + val builtin = result.ext.head.asInstanceOf[BindAtom].getExpression + builtin should (be(a[FunctionCall]) and have( + 'functionName ("SKOLEM") + )) + } + + (class_OWLObjectSomeValuesFrom.toString ++ " w/ constant skolemization") should + "be converted into a RDFoxRuleShards" in { + val skolem = SkolemStrategy.Constant(class_OWLObjectSomeValuesFrom.toString) + val visitor = RDFoxClassExprConverter(term_x, List(), skolem) + val result = class_OWLObjectSomeValuesFrom.accept(visitor) + result shouldBe a[RDFoxRuleShards] + } + + it should "have exactly two TupleTableAtoms in its result list" in { + val skolem = SkolemStrategy.Constant(class_OWLObjectSomeValuesFrom.toString) + val visitor = RDFoxClassExprConverter(term_x, List(), skolem) + val result = class_OWLObjectSomeValuesFrom.accept(visitor) + exactly(2, result.res) should (be(an[TupleTableAtom]) + //and have('numberOfArguments (3)) + ) + } + + it should "have an empty extension list" in { + val skolem = SkolemStrategy.Constant(class_OWLObjectSomeValuesFrom.toString) + val visitor = RDFoxClassExprConverter(term_x, List(), skolem) + val result = class_OWLObjectSomeValuesFrom.accept(visitor) + result.ext shouldBe empty + } + + // OWLObjectMaxCardinalityImpl + class_OWLObjectMaxCardinality.toString should + "be converted into a RDFoxRuleShards" in { + val visitor = RDFoxClassExprConverter(term_x) + val result = class_OWLObjectMaxCardinality.accept(visitor) + result shouldBe a[RDFoxRuleShards] + } + + // it should "have a single TupleTableAtom in the result list" in { + // val visitor = RDFoxClassExprConverter(term_x) + // val result = class_OWLObjectMaxCardinality.accept(visitor) + // result.res.loneElement should (be(an[TupleTableAtom]) and have( + // 'tupleTableName (pred_sameAs) + // )) + // } + + it should "have 4 TupleTableAtoms in its extension list" in { + val visitor = RDFoxClassExprConverter(term_x) + val result = class_OWLObjectMaxCardinality.accept(visitor) + exactly(4, result.ext) should (be(an[TupleTableAtom]) + //and have('numberOfArguments (3)) + ) + } + +} // class OWLClassSpec -- cgit v1.2.3