Skip to content

Commit 4f12471

Browse files
Copilothmottestad
andcommitted
Add comprehensive @base tests for duplicate relative prefix declarations
Co-authored-by: hmottestad <797185+hmottestad@users.noreply.github.com>
1 parent 7ad6f52 commit 4f12471

1 file changed

Lines changed: 201 additions & 1 deletion

File tree

core/rio/turtle/src/test/java/org/eclipse/rdf4j/rio/turtle/TurtlePrefixDuplicateTest.java

Lines changed: 201 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,23 @@
1212

1313
import static org.assertj.core.api.Assertions.assertThat;
1414
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
15+
import static org.junit.jupiter.api.Assertions.assertThrows;
1516

1617
import java.io.StringReader;
1718
import java.util.ArrayList;
1819
import java.util.List;
1920

2021
import org.eclipse.rdf4j.model.Statement;
2122
import org.eclipse.rdf4j.rio.RDFHandler;
23+
import org.eclipse.rdf4j.rio.RDFParseException;
2224
import org.eclipse.rdf4j.rio.RDFParser;
2325
import org.eclipse.rdf4j.rio.helpers.StatementCollector;
2426
import org.junit.jupiter.api.BeforeEach;
2527
import org.junit.jupiter.api.Test;
2628

2729
/**
28-
* Tests for duplicate prefix declarations in Turtle parser.
30+
* Tests for duplicate prefix declarations in Turtle parser. Tests include scenarios with @base to show how different
31+
* placement of the base changes duplicate relative prefix declarations.
2932
*/
3033
public class TurtlePrefixDuplicateTest {
3134

@@ -85,4 +88,201 @@ public void testDuplicateDefaultPrefixDeclarations_SameNamespace_ShouldPass() th
8588
assertThat(statements).hasSize(1);
8689
assertThat(statements.get(0).getPredicate().toString()).isEqualTo("http://example.org/ns#name");
8790
}
91+
92+
// Tests with @base showing different scenarios with relative prefix declarations
93+
94+
@Test
95+
public void testDuplicateRelativePrefix_SameBase_ShouldPass() throws Exception {
96+
String turtle = "@base <http://example.org/> .\n" +
97+
"@prefix rel: <vocab/> .\n" +
98+
"@prefix rel: <vocab/> .\n" +
99+
"\n" +
100+
"<http://example.org/person> rel:name \"Alice\" .\n";
101+
102+
// Should not throw an exception when same relative prefix resolves to same absolute namespace
103+
assertDoesNotThrow(() -> parser.parse(new StringReader(turtle), "http://example.org/"));
104+
105+
// Should produce the expected statement with resolved absolute namespace
106+
assertThat(statements).hasSize(1);
107+
assertThat(statements.get(0).getPredicate().toString()).isEqualTo("http://example.org/vocab/name");
108+
}
109+
110+
@Test
111+
public void testDuplicateRelativePrefix_BaseChangedBetween_LastWins() throws Exception {
112+
String turtle = "@base <http://example.org/> .\n" +
113+
"@prefix rel: <vocab/> .\n" +
114+
"@base <http://different.org/> .\n" +
115+
"@prefix rel: <vocab/> .\n" +
116+
"\n" +
117+
"<http://example.org/person> rel:name \"Bob\" .\n";
118+
119+
// Should not throw exception - turtle allows redefinition, last one wins
120+
assertDoesNotThrow(() -> parser.parse(new StringReader(turtle), "http://example.org/"));
121+
122+
// The last prefix declaration should win (resolved with the second base)
123+
assertThat(statements).hasSize(1);
124+
assertThat(statements.get(0).getPredicate().toString()).isEqualTo("http://different.org/vocab/name");
125+
}
126+
127+
@Test
128+
public void testDuplicateRelativePrefix_ExternalBaseChange_LastWins() throws Exception {
129+
String turtle = "@prefix rel: <vocab/> .\n" +
130+
"@base <http://different.org/> .\n" +
131+
"@prefix rel: <vocab/> .\n" +
132+
"\n" +
133+
"<http://example.org/person> rel:name \"Charlie\" .\n";
134+
135+
// Should not throw exception - turtle allows redefinition
136+
assertDoesNotThrow(() -> parser.parse(new StringReader(turtle), "http://example.org/"));
137+
138+
// First prefix uses external base, second uses internal base
139+
assertThat(statements).hasSize(1);
140+
assertThat(statements.get(0).getPredicate().toString()).isEqualTo("http://different.org/vocab/name");
141+
}
142+
143+
@Test
144+
public void testDuplicateRelativePrefix_MultipleBaseChanges_LastWins() throws Exception {
145+
String turtle = "@base <http://first.org/> .\n" +
146+
"@prefix rel: <vocab/> .\n" +
147+
"@base <http://second.org/> .\n" +
148+
"@base <http://third.org/> .\n" +
149+
"@prefix rel: <vocab/> .\n" +
150+
"\n" +
151+
"<http://example.org/person> rel:name \"David\" .\n";
152+
153+
// Should not throw exception
154+
assertDoesNotThrow(() -> parser.parse(new StringReader(turtle), "http://example.org/"));
155+
156+
// Last prefix with last base should win
157+
assertThat(statements).hasSize(1);
158+
assertThat(statements.get(0).getPredicate().toString()).isEqualTo("http://third.org/vocab/name");
159+
}
160+
161+
@Test
162+
public void testDuplicateRelativePrefix_BaseAfterAllPrefixes_EarlierBasesUsed() throws Exception {
163+
String turtle = "@base <http://first.org/> .\n" +
164+
"@prefix rel: <vocab/> .\n" +
165+
"@base <http://second.org/> .\n" +
166+
"@prefix rel: <vocab/> .\n" +
167+
"@base <http://third.org/> .\n" +
168+
"\n" +
169+
"<http://example.org/person> rel:name \"Eve\" .\n";
170+
171+
// Should not throw exception
172+
assertDoesNotThrow(() -> parser.parse(new StringReader(turtle), "http://example.org/"));
173+
174+
// Second prefix with second base should be used (base after prefixes doesn't affect them)
175+
assertThat(statements).hasSize(1);
176+
assertThat(statements.get(0).getPredicate().toString()).isEqualTo("http://second.org/vocab/name");
177+
}
178+
179+
@Test
180+
public void testDuplicateRelativePrefix_SameExternalBase_ShouldPass() throws Exception {
181+
String turtle = "@prefix rel: <vocab/> .\n" +
182+
"@prefix rel: <vocab/> .\n" +
183+
"\n" +
184+
"<http://example.org/person> rel:name \"Frank\" .\n";
185+
186+
// Should not throw exception when both relative prefixes resolve to same namespace using external base
187+
assertDoesNotThrow(() -> parser.parse(new StringReader(turtle), "http://example.org/"));
188+
189+
// Both prefixes resolve to same namespace using external base
190+
assertThat(statements).hasSize(1);
191+
assertThat(statements.get(0).getPredicate().toString()).isEqualTo("http://example.org/vocab/name");
192+
}
193+
194+
@Test
195+
public void testDuplicateRelativePrefix_SameInternalBase_ShouldPass() throws Exception {
196+
String turtle = "@base <http://example.org/ns/> .\n" +
197+
"@prefix rel: <vocab/> .\n" +
198+
"@prefix rel: <vocab/> .\n" +
199+
"\n" +
200+
"<http://example.org/person> rel:name \"Grace\" .\n";
201+
202+
// Should not throw exception when both relative prefixes resolve to same namespace
203+
assertDoesNotThrow(() -> parser.parse(new StringReader(turtle), "http://example.org/"));
204+
205+
// Both prefixes resolve to same namespace using internal base
206+
assertThat(statements).hasSize(1);
207+
assertThat(statements.get(0).getPredicate().toString()).isEqualTo("http://example.org/ns/vocab/name");
208+
}
209+
210+
@Test
211+
public void testDuplicateRelativePrefix_AbsoluteToRelative_LastWins() throws Exception {
212+
String turtle = "@prefix rel: <http://absolute.org/vocab/> .\n" +
213+
"@base <http://relative.org/> .\n" +
214+
"@prefix rel: <vocab/> .\n" +
215+
"\n" +
216+
"<http://example.org/person> rel:name \"Henry\" .\n";
217+
218+
// Should not throw exception
219+
assertDoesNotThrow(() -> parser.parse(new StringReader(turtle), "http://example.org/"));
220+
221+
// Second (relative) prefix should win
222+
assertThat(statements).hasSize(1);
223+
assertThat(statements.get(0).getPredicate().toString()).isEqualTo("http://relative.org/vocab/name");
224+
}
225+
226+
@Test
227+
public void testDuplicateRelativePrefix_RelativeToAbsolute_LastWins() throws Exception {
228+
String turtle = "@base <http://relative.org/> .\n" +
229+
"@prefix rel: <vocab/> .\n" +
230+
"@prefix rel: <http://absolute.org/vocab/> .\n" +
231+
"\n" +
232+
"<http://example.org/person> rel:name \"Ivy\" .\n";
233+
234+
// Should not throw exception
235+
assertDoesNotThrow(() -> parser.parse(new StringReader(turtle), "http://example.org/"));
236+
237+
// Second (absolute) prefix should win
238+
assertThat(statements).hasSize(1);
239+
assertThat(statements.get(0).getPredicate().toString()).isEqualTo("http://absolute.org/vocab/name");
240+
}
241+
242+
@Test
243+
public void testDuplicateRelativePrefix_ComplexRelativePaths_LastWins() throws Exception {
244+
String turtle = "@base <http://example.org/path/> .\n" +
245+
"@prefix rel: <../vocab/> .\n" +
246+
"@base <http://example.org/different/path/> .\n" +
247+
"@prefix rel: <../../vocab/> .\n" +
248+
"\n" +
249+
"<http://example.org/person> rel:name \"Jack\" .\n";
250+
251+
// Should not throw exception
252+
assertDoesNotThrow(() -> parser.parse(new StringReader(turtle), "http://example.org/"));
253+
254+
// Last prefix with complex relative path should win
255+
assertThat(statements).hasSize(1);
256+
assertThat(statements.get(0).getPredicate().toString()).isEqualTo("http://example.org/vocab/name");
257+
}
258+
259+
@Test
260+
public void testDuplicateRelativePrefix_NoBaseForRelative_ShouldFail() throws Exception {
261+
String turtle = "@prefix rel: <vocab/> .\n" +
262+
"@prefix rel: <vocab/> .\n" +
263+
"\n" +
264+
"<http://example.org/person> rel:name \"Kate\" .\n";
265+
266+
// Should throw exception when relative prefix cannot be resolved (no base provided)
267+
assertThrows(RDFParseException.class, () -> {
268+
parser.parse(new StringReader(turtle), null);
269+
});
270+
}
271+
272+
@Test
273+
public void testDuplicateDefaultRelativePrefix_BaseChanges_LastWins() throws Exception {
274+
String turtle = "@base <http://first.org/> .\n" +
275+
"@prefix : <vocab/> .\n" +
276+
"@base <http://second.org/> .\n" +
277+
"@prefix : <vocab/> .\n" +
278+
"\n" +
279+
"<http://example.org/person> :name \"Luna\" .\n";
280+
281+
// Should not throw exception
282+
assertDoesNotThrow(() -> parser.parse(new StringReader(turtle), "http://example.org/"));
283+
284+
// Last default prefix declaration should win
285+
assertThat(statements).hasSize(1);
286+
assertThat(statements.get(0).getPredicate().toString()).isEqualTo("http://second.org/vocab/name");
287+
}
88288
}

0 commit comments

Comments
 (0)