Skip to content

Commit 3d4fb79

Browse files
committed
more tests
1 parent ebbe896 commit 3d4fb79

2 files changed

Lines changed: 173 additions & 44 deletions

File tree

core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/FoafCliqueLftjCorrectnessTest.java

Lines changed: 155 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -14,74 +14,129 @@
1414
import static org.junit.jupiter.api.Assertions.assertEquals;
1515

1616
import java.io.File;
17+
import java.io.IOException;
18+
import java.nio.charset.StandardCharsets;
19+
import java.nio.file.Files;
1720
import java.nio.file.Path;
21+
import java.security.MessageDigest;
22+
import java.security.NoSuchAlgorithmException;
23+
import java.util.ArrayList;
24+
import java.util.EnumMap;
25+
import java.util.LinkedHashMap;
1826
import java.util.List;
27+
import java.util.Map;
28+
import java.util.stream.Collectors;
29+
import java.util.stream.Stream;
1930

31+
import org.apache.commons.io.FileUtils;
32+
import org.eclipse.rdf4j.model.Value;
33+
import org.eclipse.rdf4j.query.BindingSet;
2034
import org.eclipse.rdf4j.repository.Repository;
2135
import org.eclipse.rdf4j.repository.RepositoryConnection;
2236
import org.eclipse.rdf4j.repository.sail.SailRepository;
2337
import org.eclipse.rdf4j.repository.sail.SailRepositoryConnection;
24-
import org.eclipse.rdf4j.sail.lmdb.LmdbStore;
38+
import org.eclipse.rdf4j.rio.helpers.NTriplesUtil;
39+
import org.eclipse.rdf4j.sail.lmdb.LmdbLftjBenchmarkMode;
2540
import org.eclipse.rdf4j.sail.lmdb.benchmark.FoafCliqueQueryCatalog.QueryScenario;
26-
import org.eclipse.rdf4j.sail.lmdb.config.LmdbStoreConfig;
41+
import org.junit.jupiter.api.AfterAll;
42+
import org.junit.jupiter.api.BeforeAll;
2743
import org.junit.jupiter.api.Test;
28-
import org.junit.jupiter.api.io.TempDir;
44+
import org.junit.jupiter.api.TestInstance;
45+
import org.junit.jupiter.params.ParameterizedTest;
46+
import org.junit.jupiter.params.provider.Arguments;
47+
import org.junit.jupiter.params.provider.MethodSource;
2948

49+
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
3050
class FoafCliqueLftjCorrectnessTest {
3151

52+
private static final int PEOPLE_COUNT = 300;
53+
private static final int CLIQUE_PERCENTAGE = 30;
54+
private static final int MIN_CLIQUE_SIZE = 3;
55+
private static final int MAX_CLIQUE_SIZE = 6;
56+
private static final int RANDOM_KNOWS_EDGES = 900;
57+
private static final long SEED = 12345L;
58+
59+
private final Map<String, Repository> repositories = new LinkedHashMap<>();
60+
private final Map<QueryScenario, Map<String, String>> queryHashes = new EnumMap<>(QueryScenario.class);
61+
62+
private Path tempDir;
63+
64+
@BeforeAll
65+
void setUp() throws IOException {
66+
tempDir = Files.createTempDirectory("rdf4j-lmdb-foaf-clique-correctness");
67+
for (String benchmarkMode : FoafCliqueQueryBenchmark.benchmarkModes()) {
68+
Repository repository = createRepository(tempDir.resolve(benchmarkMode).toFile(), benchmarkMode);
69+
populate(repository);
70+
repositories.put(benchmarkMode, repository);
71+
}
72+
for (QueryScenario scenario : FoafCliqueQueryCatalog.allScenarios()) {
73+
Map<String, String> hashesByMode = new LinkedHashMap<>();
74+
for (String benchmarkMode : FoafCliqueQueryBenchmark.benchmarkModes()) {
75+
hashesByMode.put(benchmarkMode, executeResultSetHash(repositoryFor(benchmarkMode), scenario.query()));
76+
}
77+
queryHashes.put(scenario, hashesByMode);
78+
}
79+
}
80+
81+
@AfterAll
82+
void tearDown() throws IOException {
83+
for (Repository repository : repositories.values()) {
84+
repository.shutDown();
85+
}
86+
if (tempDir != null) {
87+
FileUtils.deleteDirectory(tempDir.toFile());
88+
}
89+
}
90+
3291
@Test
33-
void baselineCycleQueriesShouldMatchRegularJoinCount(@TempDir Path tempDir) {
34-
assertQueriesMatch(tempDir, FoafCliqueQueryCatalog.baselineScenarios());
92+
void baselineCycleQueriesShouldMatchRegularJoinCount() {
93+
assertQueriesMatch(FoafCliqueQueryCatalog.baselineScenarios());
3594
}
3695

3796
@Test
38-
void mixedCycleQueriesShouldMatchRegularJoinCount(@TempDir Path tempDir) {
39-
assertQueriesMatch(tempDir, FoafCliqueQueryCatalog.mixedScenarios());
97+
void mixedCycleQueriesShouldMatchRegularJoinCount() {
98+
assertQueriesMatch(FoafCliqueQueryCatalog.mixedScenarios());
4099
}
41100

42-
private void assertQueriesMatch(Path tempDir, List<QueryScenario> scenarios) {
43-
Repository fallbackRepository = createRepository(tempDir.resolve("fallback").toFile(), false, false);
44-
Repository interpretedRepository = createRepository(tempDir.resolve("interpreted").toFile(), true, false);
45-
Repository compiledRepository = createRepository(tempDir.resolve("compiled").toFile(), true, true);
101+
@ParameterizedTest(name = "{0} / {1}")
102+
@MethodSource("queryAndModeArguments")
103+
void eachQueryAndModeShouldProduceTheSameResultHash(QueryScenario scenario, String benchmarkMode) {
104+
assertEquals(hashFor(scenario, FoafCliqueQueryBenchmark.LFTJ_DISABLED), hashFor(scenario, benchmarkMode),
105+
benchmarkMode + " must preserve the full result set for " + scenario.benchmarkMethodName());
106+
}
46107

47-
try {
48-
populate(fallbackRepository);
49-
populate(interpretedRepository);
50-
populate(compiledRepository);
51-
52-
for (QueryScenario scenario : scenarios) {
53-
long expected = executeCount(fallbackRepository, scenario.query());
54-
long interpreted = executeCount(interpretedRepository, scenario.query());
55-
long compiled = executeCount(compiledRepository, scenario.query());
56-
57-
assertEquals(expected, interpreted,
58-
"Interpreted LFTJ must preserve the " + scenario.benchmarkMethodName() + " result count");
59-
assertEquals(expected, compiled,
60-
"Compiled LFTJ must preserve the " + scenario.benchmarkMethodName() + " result count");
108+
static Stream<Arguments> queryAndModeArguments() {
109+
return FoafCliqueQueryCatalog.allScenarios()
110+
.stream()
111+
.flatMap(scenario -> FoafCliqueQueryBenchmark.benchmarkModes()
112+
.stream()
113+
.map(benchmarkMode -> Arguments.of(scenario, benchmarkMode)));
114+
}
115+
116+
private void assertQueriesMatch(List<QueryScenario> scenarios) {
117+
Repository fallbackRepository = repositoryFor(FoafCliqueQueryBenchmark.LFTJ_DISABLED);
118+
for (QueryScenario scenario : scenarios) {
119+
long expected = executeCount(fallbackRepository, scenario.query());
120+
for (String benchmarkMode : List.of(
121+
LmdbLftjBenchmarkMode.INTERPRETED,
122+
LmdbLftjBenchmarkMode.EXECUTOR_CODEGEN,
123+
LmdbLftjBenchmarkMode.FULL_CODEGEN)) {
124+
assertEquals(expected, executeCount(repositoryFor(benchmarkMode), scenario.query()),
125+
benchmarkMode + " must preserve the " + scenario.benchmarkMethodName() + " result count");
61126
}
62-
} finally {
63-
fallbackRepository.shutDown();
64-
interpretedRepository.shutDown();
65-
compiledRepository.shutDown();
66127
}
67128
}
68129

69-
private Repository createRepository(File dataDir, boolean lftjEnabled, boolean lftjCodegenEnabled) {
70-
LmdbStoreConfig config = new LmdbStoreConfig("spoc,sopc,psoc,posc,ospc,opsc");
71-
config.setLftjEnabled(lftjEnabled);
72-
config.setLftjCodegenEnabled(lftjCodegenEnabled);
73-
config.setForceSync(false);
74-
config.setValueDBSize(1_073_741_824L);
75-
config.setTripleDBSize(config.getValueDBSize());
76-
77-
Repository repository = new SailRepository(new LmdbStore(dataDir, config));
130+
private Repository createRepository(File dataDir, String benchmarkMode) {
131+
SailRepository repository = FoafCliqueQueryBenchmark.createRepository(dataDir, benchmarkMode);
78132
repository.init();
79133
return repository;
80134
}
81135

82136
private void populate(Repository repository) {
83137
try (SailRepositoryConnection connection = (SailRepositoryConnection) repository.getConnection()) {
84-
new FoafCliqueDataGenerator(300, 30, 3, 6, 900, 12345L).populate(connection);
138+
new FoafCliqueDataGenerator(PEOPLE_COUNT, CLIQUE_PERCENTAGE, MIN_CLIQUE_SIZE, MAX_CLIQUE_SIZE,
139+
RANDOM_KNOWS_EDGES, SEED).populate(connection);
85140
}
86141
}
87142

@@ -90,4 +145,65 @@ private long executeCount(Repository repository, String query) {
90145
return connection.prepareTupleQuery(query).evaluate().stream().count();
91146
}
92147
}
148+
149+
private String executeResultSetHash(Repository repository, String query) {
150+
List<String> rows = new ArrayList<>();
151+
try (RepositoryConnection connection = repository.getConnection()) {
152+
connection.prepareTupleQuery(query)
153+
.evaluate()
154+
.forEach(bindingSet -> rows.add(bindingSetToString(bindingSet)));
155+
}
156+
rows.sort(String::compareTo);
157+
return hash(rows);
158+
}
159+
160+
private String bindingSetToString(BindingSet bindingSet) {
161+
return bindingSet.getBindingNames()
162+
.stream()
163+
.sorted()
164+
.map(name -> name + "=" + valueToString(bindingSet.getValue(name)))
165+
.collect(Collectors.joining("|"));
166+
}
167+
168+
private String valueToString(Value value) {
169+
return NTriplesUtil.toNTriplesString(value);
170+
}
171+
172+
private String hash(List<String> rows) {
173+
MessageDigest digest = newSha256Digest();
174+
for (String row : rows) {
175+
digest.update(row.getBytes(StandardCharsets.UTF_8));
176+
digest.update((byte) '\n');
177+
}
178+
return toHex(digest.digest());
179+
}
180+
181+
private MessageDigest newSha256Digest() {
182+
try {
183+
return MessageDigest.getInstance("SHA-256");
184+
} catch (NoSuchAlgorithmException e) {
185+
throw new IllegalStateException("Missing SHA-256 digest", e);
186+
}
187+
}
188+
189+
private String toHex(byte[] bytes) {
190+
StringBuilder builder = new StringBuilder(bytes.length * 2);
191+
for (byte b : bytes) {
192+
builder.append(Character.forDigit((b >>> 4) & 0x0F, 16));
193+
builder.append(Character.forDigit(b & 0x0F, 16));
194+
}
195+
return builder.toString();
196+
}
197+
198+
private Repository repositoryFor(String benchmarkMode) {
199+
Repository repository = repositories.get(benchmarkMode);
200+
if (repository == null) {
201+
throw new IllegalArgumentException("Unknown benchmark mode: " + benchmarkMode);
202+
}
203+
return repository;
204+
}
205+
206+
private String hashFor(QueryScenario scenario, String benchmarkMode) {
207+
return queryHashes.get(scenario).get(benchmarkMode);
208+
}
93209
}

core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/FoafCliqueQueryBenchmark.java

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import java.io.File;
1515
import java.io.IOException;
1616
import java.nio.file.Files;
17+
import java.util.List;
1718
import java.util.concurrent.TimeUnit;
1819

1920
import org.apache.commons.io.FileUtils;
@@ -81,12 +82,24 @@ public static void main(String[] args) throws RunnerException {
8182
.build()).run();
8283
}
8384

85+
static List<String> benchmarkModes() {
86+
return List.of(
87+
LmdbLftjBenchmarkMode.INTERPRETED,
88+
LmdbLftjBenchmarkMode.EXECUTOR_CODEGEN,
89+
LmdbLftjBenchmarkMode.FULL_CODEGEN,
90+
LFTJ_DISABLED);
91+
}
92+
93+
static SailRepository createRepository(File dataDir, String benchmarkMode) {
94+
validateBenchmarkMode(benchmarkMode);
95+
return new SailRepository(new LmdbBenchmarkStore(dataDir, createLftjBenchmarkConfig(benchmarkMode),
96+
LFTJ_DISABLED.equals(benchmarkMode) ? null : LmdbLftjBenchmarkMode.compiler(benchmarkMode)));
97+
}
98+
8499
@Setup(Level.Trial)
85100
public void setup() throws IOException {
86-
validateBenchmarkMode(benchmarkMode);
87101
dataDir = Files.createTempDirectory("rdf4j-lmdb-foaf-cliques").toFile();
88-
repository = new SailRepository(new LmdbBenchmarkStore(dataDir, createLftjBenchmarkConfig(benchmarkMode),
89-
LFTJ_DISABLED.equals(benchmarkMode) ? null : LmdbLftjBenchmarkMode.compiler(benchmarkMode)));
102+
repository = createRepository(dataDir, benchmarkMode);
90103
repository.init();
91104

92105
try (SailRepositoryConnection connection = repository.getConnection()) {
@@ -167,7 +180,7 @@ private long executeCount(String query) {
167180
}
168181
}
169182

170-
private static LmdbStoreConfig createLftjBenchmarkConfig(String benchmarkMode) {
183+
static LmdbStoreConfig createLftjBenchmarkConfig(String benchmarkMode) {
171184
LmdbStoreConfig config = new LmdbStoreConfig("spoc,sopc,psoc,posc,ospc,opsc");
172185
boolean lftjEnabled = !LFTJ_DISABLED.equals(benchmarkMode);
173186
config.setLftjEnabled(lftjEnabled);
@@ -178,7 +191,7 @@ private static LmdbStoreConfig createLftjBenchmarkConfig(String benchmarkMode) {
178191
return config;
179192
}
180193

181-
private static void validateBenchmarkMode(String benchmarkMode) {
194+
static void validateBenchmarkMode(String benchmarkMode) {
182195
if (LFTJ_DISABLED.equals(benchmarkMode)) {
183196
return;
184197
}

0 commit comments

Comments
 (0)