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 }