diff --git a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/planNodes/ValuesBackedNode.java b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/planNodes/ValuesBackedNode.java index 4e3c8b3c623..28fc6ea9ff0 100644 --- a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/planNodes/ValuesBackedNode.java +++ b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/planNodes/ValuesBackedNode.java @@ -89,10 +89,7 @@ public IteratorData getIteratorDataType() { @Override public String toString() { return "ValuesBackedNode{" + - "collection=" + collection + - ", validationExecutionLogger=" + validationExecutionLogger + - ", printed=" + printed + - '}'; + "collection=" + collection + '}'; } @Override diff --git a/core/sail/shacl/src/test/java/org/eclipse/rdf4j/sail/shacl/AbstractShaclTest.java b/core/sail/shacl/src/test/java/org/eclipse/rdf4j/sail/shacl/AbstractShaclTest.java index 7723dc17cb3..5a8ee73d78e 100644 --- a/core/sail/shacl/src/test/java/org/eclipse/rdf4j/sail/shacl/AbstractShaclTest.java +++ b/core/sail/shacl/src/test/java/org/eclipse/rdf4j/sail/shacl/AbstractShaclTest.java @@ -389,7 +389,7 @@ static void runTestCaseSingleTransaction(String shaclPath, String dataPath, Expe SailRepository shaclRepository = getShaclSail(); try { Utils.loadShapeData(shaclRepository, shaclPath + "shacl.ttl"); - Utils.loadInitialData(shaclRepository, shaclPath + "initialData.ttl"); + Utils.loadInitialData(shaclRepository, dataPath + "initialData.ttl"); } catch (IOException e) { throw new RuntimeException(e); } @@ -460,7 +460,7 @@ static void runTestCaseRevalidate(String shaclPath, String dataPath, ExpectedRes SailRepository shaclRepository = getShaclSail(); try { Utils.loadShapeData(shaclRepository, shaclPath + "shacl.ttl"); - Utils.loadInitialData(shaclRepository, shaclPath + "initialData.ttl"); + Utils.loadInitialData(shaclRepository, dataPath + "initialData.ttl"); } catch (IOException e) { throw new RuntimeException(e); } @@ -551,17 +551,17 @@ enum ExpectedResult { private static SailRepository getShaclSail() { ShaclSail shaclSail = new ShaclSail(new MemoryStore()); - SailRepository shaclRepository = new SailRepository(shaclSail); + SailRepository repository = new SailRepository(shaclSail); shaclSail.setLogValidationPlans(fullLogging); shaclSail.setCacheSelectNodes(true); shaclSail.setParallelValidation(true); shaclSail.setLogValidationViolations(fullLogging); shaclSail.setGlobalLogValidationExecution(fullLogging); + shaclSail.setSerializableValidation(false); + repository.init(); - shaclRepository.init(); - - return shaclRepository; + return repository; } } diff --git a/core/sail/shacl/src/test/java/org/eclipse/rdf4j/sail/shacl/TargetNodeMinCountEdgeCaseTests.java b/core/sail/shacl/src/test/java/org/eclipse/rdf4j/sail/shacl/TargetNodeMinCountEdgeCaseTests.java new file mode 100644 index 00000000000..32d675ade5e --- /dev/null +++ b/core/sail/shacl/src/test/java/org/eclipse/rdf4j/sail/shacl/TargetNodeMinCountEdgeCaseTests.java @@ -0,0 +1,171 @@ +/******************************************************************************* + * Copyright (c) 2020 Eclipse RDF4J contributors. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + *******************************************************************************/ +package org.eclipse.rdf4j.sail.shacl; + +import org.eclipse.rdf4j.model.IRI; +import org.eclipse.rdf4j.model.Value; +import org.eclipse.rdf4j.model.ValueFactory; +import org.eclipse.rdf4j.model.impl.SimpleValueFactory; +import org.eclipse.rdf4j.model.vocabulary.RDF4J; +import org.eclipse.rdf4j.repository.sail.SailRepository; +import org.eclipse.rdf4j.repository.sail.SailRepositoryConnection; +import org.eclipse.rdf4j.rio.RDFFormat; +import org.eclipse.rdf4j.sail.memory.MemoryStore; +import org.junit.Test; + +import java.io.StringReader; + +/** + * Some shapes may cause a validation error even if the targets don't exist. These tests check some of those scenarios. + */ +public class TargetNodeMinCountEdgeCaseTests { + + String shaclShapes = String.join("\n", "", + "@base .", + "@prefix ex: .", + "@prefix owl: .", + "@prefix rdf: .", + "@prefix rdfs: .", + "@prefix sh: .", + "@prefix xsd: .", + "", + "ex:PersonShape", + " a sh:NodeShape ;", + " sh:targetNode ex:validPerson1, ex:validPerson2 ;", + " sh:property [", + " sh:path ex:ssn ;", + " sh:minCount 2 ;", + " ] .", + ""); + + String EX = "http://example.com/ns#"; + ValueFactory vf = SimpleValueFactory.getInstance(); + IRI validPerson1 = vf.createIRI(EX, "validPerson1"); + IRI validPerson2 = vf.createIRI(EX, "validPerson2"); + IRI ssn = vf.createIRI(EX, "ssn"); + Value value1 = vf.createLiteral(1); + Value value2 = vf.createLiteral(2); + + @Test(expected = ShaclSailValidationException.class) + public void testMinCountWithEmptyState() throws Throwable { + + SailRepository sailRepository = new SailRepository(new ShaclSail(new MemoryStore())); + + try (SailRepositoryConnection connection = sailRepository.getConnection()) { + connection.begin(); + connection.add(new StringReader(shaclShapes), "", RDFFormat.TURTLE, RDF4J.SHACL_SHAPE_GRAPH); + connection.commit(); + } catch (Exception e) { + throw e.getCause(); + } + + } + + @Test(expected = ShaclSailValidationException.class) + public void testMinCountWithInvalidInitialDataset() throws Throwable { + + SailRepository sailRepository = new SailRepository(new ShaclSail(new MemoryStore())); + + try (SailRepositoryConnection connection = sailRepository.getConnection()) { + connection.begin(); + connection.add(validPerson1, ssn, value1); + connection.add(validPerson2, ssn, value2); + connection.commit(); + + connection.begin(); + connection.add(new StringReader(shaclShapes), "", RDFFormat.TURTLE, RDF4J.SHACL_SHAPE_GRAPH); + connection.commit(); + } catch (Exception e) { + throw e.getCause(); + } + + } + + @Test(expected = ShaclSailValidationException.class) + public void testMinCountWithInvalidInitialDataset2() throws Throwable { + + SailRepository sailRepository = new SailRepository(new ShaclSail(new MemoryStore())); + + try (SailRepositoryConnection connection = sailRepository.getConnection()) { + connection.begin(); + connection.add(validPerson1, ssn, value1); + connection.add(validPerson1, ssn, value2); + connection.commit(); + + connection.begin(); + connection.add(new StringReader(shaclShapes), "", RDFFormat.TURTLE, RDF4J.SHACL_SHAPE_GRAPH); + connection.commit(); + } catch (Exception e) { + throw e.getCause(); + } + + } + + @Test(expected = ShaclSailValidationException.class) + public void testMinCountWithInvalidInitialDataset3() throws Throwable { + + SailRepository sailRepository = new SailRepository(new ShaclSail(new MemoryStore())); + + try (SailRepositoryConnection connection = sailRepository.getConnection()) { + connection.begin(); + connection.add(validPerson1, ssn, value1); + connection.add(validPerson1, ssn, value2); + connection.add(new StringReader(shaclShapes), "", RDFFormat.TURTLE, RDF4J.SHACL_SHAPE_GRAPH); + connection.commit(); + } catch (Exception e) { + throw e.getCause(); + } + + } + + @Test + public void testMinCountWithValidInitialDataset() throws Throwable { + + SailRepository sailRepository = new SailRepository(new ShaclSail(new MemoryStore())); + + try (SailRepositoryConnection connection = sailRepository.getConnection()) { + connection.begin(); + connection.add(validPerson1, ssn, value1); + connection.add(validPerson1, ssn, value2); + connection.add(validPerson2, ssn, value1); + connection.add(validPerson2, ssn, value2); + connection.commit(); + + connection.begin(); + connection.add(new StringReader(shaclShapes), "", RDFFormat.TURTLE, RDF4J.SHACL_SHAPE_GRAPH); + connection.commit(); + } catch (Exception e) { + throw e.getCause(); + } + + } + + @Test + public void testMinCountWithValidInitialDataset2() throws Throwable { + + SailRepository sailRepository = new SailRepository(new ShaclSail(new MemoryStore())); + + try (SailRepositoryConnection connection = sailRepository.getConnection()) { + connection.begin(); + connection.add(validPerson1, ssn, value1); + connection.add(validPerson1, ssn, value2); + + connection.commit(); + + connection.begin(); + connection.add(new StringReader(shaclShapes), "", RDFFormat.TURTLE, RDF4J.SHACL_SHAPE_GRAPH); + connection.add(validPerson2, ssn, value1); + connection.add(validPerson2, ssn, value2); + connection.commit(); + } catch (Exception e) { + throw e.getCause(); + } + + } + +} diff --git a/core/sail/shacl/src/test/resources/test-cases/minCount/targetNode/initialData.ttl b/core/sail/shacl/src/test/resources/test-cases/minCount/targetNode/initialData.ttl deleted file mode 100644 index 5ac82aa9735..00000000000 --- a/core/sail/shacl/src/test/resources/test-cases/minCount/targetNode/initialData.ttl +++ /dev/null @@ -1,11 +0,0 @@ -@base . -@prefix ex: . -@prefix owl: . -@prefix rdf: . -@prefix rdfs: . -@prefix sh: . -@prefix xsd: . - - -ex:validPerson1 ex:ssn 1 , 2. -ex:validPerson2 ex:ssn 1 , 2.