1 package eu.fbk.rdfpro.util; 2 3 import static org.junit.Assert.assertEquals; 4 import static org.junit.Assert.assertFalse; 5 import static org.junit.Assert.assertTrue; 6 import static org.junit.Assert.fail; 7 8 import java.nio.file.Files; 9 import java.nio.file.Path; 10 import java.util.Arrays; 11 import java.util.Collection; 12 import java.util.HashSet; 13 import java.util.Iterator; 14 import java.util.Set; 15 import java.util.concurrent.TimeUnit; 16 17 import com.google.common.base.Throwables; 18 import com.google.common.collect.ImmutableList; 19 import com.google.common.collect.ImmutableSet; 20 21 import org.junit.After; 22 import org.junit.Before; 23 import org.junit.Rule; 24 import org.junit.Test; 25 import org.junit.rules.ExpectedException; 26 import org.junit.rules.Timeout; 27 import org.junit.runner.RunWith; 28 import org.junit.runners.Parameterized; 29 import org.junit.runners.Parameterized.Parameters; 30 import org.openrdf.IsolationLevels; 31 import org.openrdf.model.BNode; 32 import org.openrdf.model.Literal; 33 import org.openrdf.model.Resource; 34 import org.openrdf.model.Statement; 35 import org.openrdf.model.URI; 36 import org.openrdf.model.Value; 37 import org.openrdf.model.ValueFactory; 38 import org.openrdf.model.impl.ContextStatementImpl; 39 import org.openrdf.model.impl.LinkedHashModel; 40 import org.openrdf.model.impl.NamespaceImpl; 41 import org.openrdf.model.impl.TreeModel; 42 import org.openrdf.model.impl.ValueFactoryImpl; 43 import org.openrdf.model.vocabulary.RDFS; 44 import org.openrdf.query.BindingSet; 45 import org.openrdf.query.MalformedQueryException; 46 import org.openrdf.query.algebra.TupleExpr; 47 import org.openrdf.query.impl.ListBindingSet; 48 import org.openrdf.repository.Repository; 49 import org.openrdf.repository.RepositoryConnection; 50 import org.openrdf.repository.sail.SailRepository; 51 import org.openrdf.sail.SailConnection; 52 import org.openrdf.sail.memory.MemoryStore; 53 54 import eu.fbk.rdfpro.util.Algebra; 55 import eu.fbk.rdfpro.util.IO; 56 import eu.fbk.rdfpro.util.QuadModel; 57 58 @RunWith(Parameterized.class) 59 public final class QuadModelTest { 60 61 @Rule 62 public Timeout timeout = new Timeout(1000, TimeUnit.MILLISECONDS); 63 64 @Rule 65 public ExpectedException thrown = ExpectedException.none(); 66 67 private final String parameter; 68 69 private Literal literal1; 70 71 private Literal literal2; 72 73 private URI uri1; 74 75 private URI uri2; 76 77 private BNode bnode1; 78 79 private BNode bnode2; 80 81 private URI ctx1; 82 83 private URI ctx2; 84 85 @Parameters 86 public static Collection<String> parameters() { 87 return Arrays.asList(new String[] { "memory", "sail", "repository", "hash", "tree" }); 88 } 89 90 public QuadModelTest(final String parameter) { 91 this.parameter = parameter; 92 } 93 94 private QuadModel newModel() { 95 try { 96 switch (this.parameter) { 97 case "memory": { 98 return QuadModel.create(); 99 } 100 case "sail": { 101 final Path path = Files.createTempDirectory("sailmodel"); 102 path.toFile().deleteOnExit(); 103 final MemoryStore sail = new MemoryStore(path.toFile()); 104 sail.setPersist(false); 105 sail.initialize(); 106 final SailConnection connection = sail.getConnection(); 107 connection.begin(IsolationLevels.NONE); 108 return QuadModel.wrap(connection, true); 109 } 110 case "repository": { 111 final Path path = Files.createTempDirectory("sailmodel"); 112 path.toFile().deleteOnExit(); 113 final MemoryStore sail = new MemoryStore(path.toFile()); 114 sail.setPersist(false); 115 final Repository repository = new SailRepository(sail); 116 repository.initialize(); 117 final RepositoryConnection connection = repository.getConnection(); 118 connection.begin(); 119 return QuadModel.wrap(connection, true); 120 } 121 case "hash": { 122 return QuadModel.wrap(new LinkedHashModel()); 123 } 124 case "tree": { 125 return QuadModel.wrap(new TreeModel()); 126 } 127 default: 128 throw new Error(); 129 } 130 } catch (final Throwable ex) { 131 throw Throwables.propagate(ex); 132 } 133 } 134 135 private void disposeModel(final QuadModel model) { 136 IO.closeQuietly(model); 137 } 138 139 @Before 140 public void setUp() { 141 final ValueFactory vf = ValueFactoryImpl.getInstance(); 142 this.uri1 = vf.createURI("urn:test:uri:1"); 143 this.uri2 = vf.createURI("urn:test:uri:2"); 144 this.bnode1 = vf.createBNode("bnode1"); 145 this.bnode2 = vf.createBNode("bnode2"); 146 this.literal1 = vf.createLiteral("test literal 1"); 147 this.literal2 = vf.createLiteral("test literal 2"); 148 this.ctx1 = vf.createURI("urn:test:ctx:1"); 149 this.ctx2 = vf.createURI("urn:test:ctx:2"); 150 } 151 152 @After 153 public void tearDown() { 154 } 155 156 @Test 157 public final void testEmpty() { 158 final QuadModel model = newModel(); 159 try { 160 assertEquals(0, model.size()); 161 assertEquals(null, model.objectValue()); 162 assertEquals(null, model.objectLiteral()); 163 assertEquals(null, model.objectResource()); 164 assertEquals(null, model.objectURI()); 165 assertEquals(null, model.objectString()); 166 } finally { 167 disposeModel(model); 168 } 169 } 170 171 @Test 172 public final void testSingleLiteral() { 173 final QuadModel model = newModel(); 174 try { 175 model.add(this.uri1, RDFS.LABEL, this.literal1, this.ctx1); 176 assertEquals(1, model.size()); 177 assertEquals( 178 new HashSet<Statement>(ImmutableSet.of(new ContextStatementImpl(this.uri1, 179 RDFS.LABEL, this.literal1, this.ctx1))), new HashSet<Statement>(model)); 180 assertTrue(model.contains(null, null, this.literal1)); 181 assertTrue(model.contains(null, null, this.literal1, this.ctx1)); 182 assertFalse(model.filter(null, null, this.literal1).isEmpty()); 183 assertFalse(model.filter(null, null, this.literal1, this.ctx1).isEmpty()); 184 assertTrue(model.filter(null, null, this.literal1, (Resource) null).isEmpty()); 185 assertEquals(this.literal1, model.objectValue()); 186 assertEquals(this.literal1, model.objectLiteral()); 187 assertEquals(this.literal1.stringValue(), model.objectString()); 188 assertThrown(Throwable.class, () -> { 189 model.objectResource(); 190 }); 191 assertThrown(Throwable.class, () -> { 192 model.objectURI(); 193 }); 194 } finally { 195 disposeModel(model); 196 } 197 } 198 199 @Test 200 public final void testSingleURI() { 201 final QuadModel model = newModel(); 202 try { 203 final Statement stmt = new ContextStatementImpl(this.uri1, RDFS.LABEL, this.uri2, 204 this.ctx1); 205 model.add(stmt); 206 assertTrue(model.contains(this.uri1, RDFS.LABEL, this.uri2, this.ctx1)); 207 assertTrue(model.contains(this.uri1, RDFS.LABEL, this.uri2)); 208 assertTrue(model.contains(stmt)); 209 assertTrue(ImmutableSet.copyOf(model).equals(ImmutableSet.of(stmt))); 210 assertEquals(1, model.size()); 211 assertEquals(this.uri2, model.objectValue()); 212 assertEquals(this.uri2, model.objectResource()); 213 assertEquals(this.uri2, model.objectURI()); 214 assertThrown(Throwable.class, () -> { 215 model.objectLiteral(); 216 }); 217 } finally { 218 disposeModel(model); 219 } 220 } 221 222 @Test 223 public final void testSingleBNode() { 224 final QuadModel model = newModel(); 225 try { 226 model.add(this.uri1, RDFS.LABEL, this.bnode1, this.ctx1); 227 assertEquals(1, model.size()); 228 assertEquals(this.bnode1, model.objectValue()); 229 assertEquals(this.bnode1, model.objectResource()); 230 assertEquals(this.bnode1.stringValue(), model.objectString()); 231 assertThrown(Throwable.class, () -> { 232 model.objectLiteral(); 233 }); 234 assertThrown(Throwable.class, () -> { 235 model.objectURI(); 236 }); 237 } finally { 238 disposeModel(model); 239 } 240 } 241 242 @Test 243 public final void testMultiple() { 244 for (final Value obj1 : new Value[] { this.uri1, this.bnode1, this.literal1 }) { 245 for (final Value obj2 : new Value[] { this.uri2, this.bnode2, this.literal2 }) { 246 final QuadModel model = newModel(); 247 try { 248 assertEquals(true, model.isEmpty()); 249 assertEquals(0, model.size()); 250 model.add(this.uri1, RDFS.LABEL, obj1, this.ctx1); 251 model.add(this.uri1, RDFS.LABEL, obj2, this.ctx2); 252 assertEquals(false, model.isEmpty()); 253 assertEquals(2, model.size()); 254 assertEquals(2, model.size(null, RDFS.LABEL, null)); 255 assertEquals(1, model.size(null, RDFS.LABEL, null, this.ctx1)); 256 assertEquals(1, model.size(null, RDFS.LABEL, null, this.ctx2)); 257 assertEquals(1, model.size(this.uri1, RDFS.LABEL, obj2, this.ctx2)); 258 assertEquals(0, model.size(this.uri2, null, null)); 259 assertEquals(2, model.size(this.uri1, null, null, this.ctx1, this.ctx2)); 260 assertEquals(ImmutableSet.of(RDFS.LABEL), model.predicates()); 261 assertEquals(ImmutableSet.of(this.uri1), model.subjects()); 262 assertEquals(ImmutableSet.of(obj1, obj2), model.objects()); 263 assertEquals(ImmutableSet.of(this.ctx1, this.ctx2), model.contexts()); 264 assertThrown(Throwable.class, () -> { 265 model.objectLiteral(); 266 }); 267 assertThrown(Throwable.class, () -> { 268 model.objectURI(); 269 }); 270 assertThrown(Throwable.class, () -> { 271 model.objectValue(); 272 }); 273 assertThrown(Throwable.class, () -> { 274 model.objectResource(); 275 }); 276 assertThrown(Throwable.class, () -> { 277 model.objectString(); 278 }); 279 final Set<Statement> set = new HashSet<>(); 280 set.add(new ContextStatementImpl(this.uri1, RDFS.LABEL, obj1, this.ctx1)); 281 set.add(new ContextStatementImpl(this.uri1, RDFS.LABEL, obj2, this.ctx2)); 282 Set<Statement> actual = ImmutableSet.copyOf(model); 283 assertEquals(set, actual); 284 assertFalse(model.remove(null, null, obj2, this.ctx1)); 285 assertEquals(2, model.size()); 286 assertTrue(model.remove(null, null, obj2, this.ctx2)); 287 assertEquals(1, model.size()); 288 set.clear(); 289 set.add(new ContextStatementImpl(this.uri1, RDFS.LABEL, obj1, this.ctx1)); 290 actual = ImmutableSet.copyOf(model); 291 assertEquals(set, actual); 292 model.clear(); 293 assertEquals(0, model.size()); 294 } finally { 295 disposeModel(model); 296 } 297 } 298 } 299 } 300 301 @Test 302 public final void testNamespaces() { 303 final QuadModel model = newModel(); 304 assertEquals(0, model.getNamespaces().size()); 305 model.setNamespace("test", "urn:test"); 306 assertEquals(1, model.getNamespaces().size()); 307 assertEquals(new NamespaceImpl("test", "urn:test"), model.getNamespace("test")); 308 model.setNamespace(new NamespaceImpl("test", "urn:test2")); 309 assertEquals(1, model.getNamespaces().size()); 310 assertEquals(new NamespaceImpl("test", "urn:test2"), model.getNamespace("test")); 311 model.removeNamespace("test"); 312 assertEquals(0, model.getNamespaces().size()); 313 assertEquals(null, model.getNamespace("test")); 314 } 315 316 @Test 317 public final void testUnmodifiable() { 318 final QuadModel model = newModel(); 319 model.add(this.uri1, RDFS.LABEL, this.bnode1, this.ctx1); 320 assertThrown(Throwable.class, () -> { 321 model.unmodifiable().add(this.uri1, RDFS.LABEL, this.literal1, this.ctx2); 322 }); 323 } 324 325 @Test 326 public final void testEvaluate() throws MalformedQueryException { 327 final String queryString = "SELECT ?s WHERE { GRAPH <" + this.ctx1 + "> { ?s ?p ?o } }"; 328 final TupleExpr expr = Algebra.parseTupleExpr(queryString, null, null); 329 final QuadModel model = newModel(); 330 model.add(this.uri1, RDFS.LABEL, this.literal1, this.ctx1); 331 model.add(this.uri1, RDFS.LABEL, this.literal2, this.ctx2); 332 final Iterator<BindingSet> iterator = model.evaluate(expr, null, new ListBindingSet( 333 ImmutableList.of("p"), RDFS.LABEL)); 334 try { 335 assertTrue(iterator.hasNext()); 336 final BindingSet bindings = iterator.next(); 337 assertEquals(1, bindings.size()); 338 assertEquals(this.uri1, bindings.getValue("s")); 339 assertFalse(iterator.hasNext()); 340 } finally { 341 IO.closeQuietly(iterator); 342 } 343 } 344 345 private static <T extends Throwable> T assertThrown(final Class<T> exceptionClazz, 346 final Runnable runnable) { 347 try { 348 runnable.run(); 349 } catch (final Throwable ex) { 350 if (exceptionClazz.isInstance(ex)) { 351 return exceptionClazz.cast(ex); 352 } 353 } 354 fail("Expected " + exceptionClazz.getName()); 355 return null; 356 } 357 358 }