Skip to content

Commit b46c916

Browse files
committed
Add more cursor tests to protect against future regression
1 parent 43ac84f commit b46c916

11 files changed

+655
-26
lines changed
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
/*
2+
* Copyright © 2016-2025 The LmdbJava Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.lmdbjava;
18+
19+
import org.junit.Test;
20+
import org.lmdbjava.ByteBufferProxy.AbstractByteBufferProxy;
21+
import org.lmdbjava.CursorIterable.KeyVal;
22+
23+
import java.io.File;
24+
import java.io.FileReader;
25+
import java.io.FileWriter;
26+
import java.io.IOException;
27+
import java.io.Reader;
28+
import java.io.Writer;
29+
import java.nio.ByteBuffer;
30+
import java.nio.file.Files;
31+
import java.nio.file.Path;
32+
import java.util.Comparator;
33+
import java.util.stream.Stream;
34+
35+
import static com.jakewharton.byteunits.BinaryByteUnit.KIBIBYTES;
36+
import static org.hamcrest.MatcherAssert.assertThat;
37+
import static org.lmdbjava.DbiFlags.MDB_CREATE;
38+
import static org.lmdbjava.DbiFlags.MDB_DUPSORT;
39+
import static org.lmdbjava.Env.create;
40+
import static org.lmdbjava.EnvFlags.MDB_NOSUBDIR;
41+
import static org.lmdbjava.TestUtils.DB_1;
42+
import static org.lmdbjava.TestUtils.POSIX_MODE;
43+
import static org.lmdbjava.TestUtils.bb;
44+
45+
/**
46+
* Test {@link CursorIterable}.
47+
*/
48+
public final class CursorIterableRangeTest {
49+
50+
@Test
51+
public void testSignedComparator() throws IOException {
52+
test(ByteBuffer::compareTo, true, "testSignedComparator", 1, MDB_CREATE);
53+
}
54+
55+
@Test
56+
public void testUnsignedComparator() throws IOException {
57+
test(AbstractByteBufferProxy::compareBuff, false, "testUnsignedComparator", 1, MDB_CREATE);
58+
}
59+
60+
@Test
61+
public void testSignedComparatorDupsort() throws IOException {
62+
test(ByteBuffer::compareTo, true, "testSignedComparatorDupsort", 2,MDB_CREATE, MDB_DUPSORT);
63+
}
64+
65+
@Test
66+
public void testUnsignedComparatorDupsort() throws IOException {
67+
test(AbstractByteBufferProxy::compareBuff, false, "testUnsignedComparatorDupsort",2, MDB_CREATE, MDB_DUPSORT);
68+
}
69+
70+
private void test(final Comparator<ByteBuffer> comparator,
71+
final boolean nativeCb,
72+
final String testName,
73+
final int copies,
74+
final DbiFlags... flags) throws IOException {
75+
final Path dbPath = Files.createTempFile("test", "db");
76+
try (final Env<ByteBuffer> env =
77+
create()
78+
.setMapSize(KIBIBYTES.toBytes(256))
79+
.setMaxReaders(1)
80+
.setMaxDbs(1)
81+
.open(dbPath.toFile(), POSIX_MODE, MDB_NOSUBDIR)) {
82+
final Dbi<ByteBuffer> dbi = env.openDbi(DB_1, comparator, nativeCb, flags);
83+
populateDatabase(env, dbi, copies);
84+
85+
final File tests = new File("src/test/resources/CursorIterableRangeTest/tests.csv");
86+
final File actual = tests.getParentFile().toPath().resolve(testName + ".actual").toFile();
87+
final File expected = tests.getParentFile().toPath().resolve(testName + ".expected").toFile();
88+
final String csv = readFile(tests);
89+
final String[] parts = csv.split("\n");
90+
try (final Writer writer = new FileWriter(actual)) {
91+
for (final String part : parts) {
92+
writer.append(part);
93+
writer.append(" =>");
94+
95+
final String[] params = part.split(",");
96+
final KeyRangeType keyRangeType = KeyRangeType.valueOf(params[0].trim());
97+
ByteBuffer start = null;
98+
ByteBuffer stop = null;
99+
if (params.length > 1 && params[1].trim().length() > 0) {
100+
start = bb(Integer.parseInt(params[1].trim()));
101+
}
102+
if (params.length > 2 && params[2].trim().length() > 0) {
103+
stop = bb(Integer.parseInt(params[2].trim()));
104+
}
105+
final KeyRange<ByteBuffer> keyRange = new KeyRange<>(keyRangeType, start, stop);
106+
boolean first = true;
107+
try (Txn<ByteBuffer> txn = env.txnRead();
108+
CursorIterable<ByteBuffer> c = dbi.iterate(txn, keyRange)) {
109+
for (final KeyVal<ByteBuffer> kv : c) {
110+
if (first) {
111+
first = false;
112+
} else {
113+
writer.append(",");
114+
}
115+
116+
final int key = kv.key().getInt();
117+
final int val = kv.val().getInt();
118+
writer.append(" [");
119+
writer.append(String.valueOf(key));
120+
writer.append(",");
121+
writer.append(String.valueOf(val));
122+
writer.append("]");
123+
}
124+
}
125+
writer.append("\n");
126+
}
127+
}
128+
129+
// Compare files.
130+
final String act = readFile(actual);
131+
final String exp = readFile(expected);
132+
assertThat("Files are not equal", act.equals(exp));
133+
} finally {
134+
deleteFile(dbPath);
135+
}
136+
}
137+
138+
private void populateDatabase(final Env<ByteBuffer> env,
139+
final Dbi<ByteBuffer> dbi,
140+
final int copies) {
141+
try (Txn<ByteBuffer> txn = env.txnWrite()) {
142+
final Cursor<ByteBuffer> c = dbi.openCursor(txn);
143+
for (int i = 0; i < copies; i++) {
144+
c.put(bb(0), bb(1 + i));
145+
c.put(bb(2), bb(3 + i));
146+
c.put(bb(4), bb(5 + i));
147+
c.put(bb(6), bb(7 + i));
148+
c.put(bb(8), bb(9 + i));
149+
c.put(bb(-2), bb(-1 + i));
150+
}
151+
txn.commit();
152+
}
153+
}
154+
155+
private String readFile(final File file) throws IOException {
156+
final StringBuilder result = new StringBuilder();
157+
try (final Reader reader = new FileReader(file)) {
158+
final char[] cbuf = new char[4096];
159+
int nread;
160+
while ((nread = reader.read(cbuf, 0, cbuf.length)) != -1) {
161+
result.append(cbuf, 0, nread);
162+
}
163+
}
164+
return result.toString();
165+
}
166+
167+
private boolean recursiveDeleteFiles(Path file) {
168+
if (deleteFile(file)) {
169+
return true;
170+
} else {
171+
try (final Stream<Path> stream = Files.list(file)) {
172+
stream.forEach(this::recursiveDeleteFiles);
173+
} catch (final IOException e) {
174+
return false;
175+
}
176+
return deleteFile(file);
177+
}
178+
}
179+
180+
private boolean deleteFile(Path file) {
181+
try {
182+
Files.delete(file);
183+
} catch (final IOException e) {
184+
return false;
185+
}
186+
return true;
187+
}
188+
}

src/test/java/org/lmdbjava/CursorIterableTest.java

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,32 @@ public final class CursorIterableTest {
7373
private Env<ByteBuffer> env;
7474
private Deque<Integer> list;
7575

76+
@Before
77+
public void before() throws IOException {
78+
final File path = tmp.newFile();
79+
env =
80+
create()
81+
.setMapSize(KIBIBYTES.toBytes(256))
82+
.setMaxReaders(1)
83+
.setMaxDbs(1)
84+
.open(path, POSIX_MODE, MDB_NOSUBDIR);
85+
db = env.openDbi(DB_1, MDB_CREATE);
86+
populateDatabase(db);
87+
}
88+
89+
private void populateDatabase(final Dbi<ByteBuffer> dbi) {
90+
list = new LinkedList<>();
91+
list.addAll(asList(2, 3, 4, 5, 6, 7, 8, 9));
92+
try (Txn<ByteBuffer> txn = env.txnWrite()) {
93+
final Cursor<ByteBuffer> c = dbi.openCursor(txn);
94+
c.put(bb(2), bb(3), MDB_NOOVERWRITE);
95+
c.put(bb(4), bb(5));
96+
c.put(bb(6), bb(7));
97+
c.put(bb(8), bb(9));
98+
txn.commit();
99+
}
100+
}
101+
76102
@After
77103
public void after() {
78104
env.close();
@@ -113,32 +139,6 @@ public void atMostTest() {
113139
verify(atMost(bb(6)), 2, 4, 6);
114140
}
115141

116-
@Before
117-
public void before() throws IOException {
118-
final File path = tmp.newFile();
119-
env =
120-
create()
121-
.setMapSize(KIBIBYTES.toBytes(256))
122-
.setMaxReaders(1)
123-
.setMaxDbs(1)
124-
.open(path, POSIX_MODE, MDB_NOSUBDIR);
125-
db = env.openDbi(DB_1, MDB_CREATE);
126-
populateDatabase(db);
127-
}
128-
129-
private void populateDatabase(final Dbi<ByteBuffer> dbi) {
130-
list = new LinkedList<>();
131-
list.addAll(asList(2, 3, 4, 5, 6, 7, 8, 9));
132-
try (Txn<ByteBuffer> txn = env.txnWrite()) {
133-
final Cursor<ByteBuffer> c = dbi.openCursor(txn);
134-
c.put(bb(2), bb(3), MDB_NOOVERWRITE);
135-
c.put(bb(4), bb(5));
136-
c.put(bb(6), bb(7));
137-
c.put(bb(8), bb(9));
138-
txn.commit();
139-
}
140-
}
141-
142142
@Test
143143
public void closedBackwardTest() {
144144
verify(closedBackward(bb(7), bb(3)), 6, 4);
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
FORWARD_ALL, , => [-2,-1], [0,1], [2,3], [4,5], [6,7], [8,9]
2+
FORWARD_AT_LEAST, 5, => [6,7], [8,9]
3+
FORWARD_AT_LEAST, 6, => [6,7], [8,9]
4+
FORWARD_AT_MOST, , 5 => [-2,-1], [0,1], [2,3], [4,5]
5+
FORWARD_AT_MOST, , 6 => [-2,-1], [0,1], [2,3], [4,5], [6,7]
6+
FORWARD_CLOSED, 3, 7 => [4,5], [6,7]
7+
FORWARD_CLOSED, 2, 6 => [2,3], [4,5], [6,7]
8+
FORWARD_CLOSED, 1, 7 => [2,3], [4,5], [6,7]
9+
FORWARD_CLOSED_OPEN, 3, 8 => [4,5], [6,7]
10+
FORWARD_CLOSED_OPEN, 2, 6 => [2,3], [4,5]
11+
FORWARD_GREATER_THAN, 4, => [6,7], [8,9]
12+
FORWARD_GREATER_THAN, 3, => [4,5], [6,7], [8,9]
13+
FORWARD_LESS_THAN, , 5 => [-2,-1], [0,1], [2,3], [4,5]
14+
FORWARD_LESS_THAN, , 8 => [-2,-1], [0,1], [2,3], [4,5], [6,7]
15+
FORWARD_OPEN, 3, 7 => [4,5], [6,7]
16+
FORWARD_OPEN, 2, 8 => [4,5], [6,7]
17+
FORWARD_OPEN_CLOSED, 3, 8 => [4,5], [6,7], [8,9]
18+
FORWARD_OPEN_CLOSED, 2, 6 => [4,5], [6,7]
19+
BACKWARD_ALL, , => [8,9], [6,7], [4,5], [2,3], [0,1], [-2,-1]
20+
BACKWARD_AT_LEAST, 5, => [4,5], [2,3], [0,1], [-2,-1]
21+
BACKWARD_AT_LEAST, 6, => [6,7], [4,5], [2,3], [0,1], [-2,-1]
22+
BACKWARD_AT_LEAST, 9, => [8,9], [6,7], [4,5], [2,3], [0,1], [-2,-1]
23+
BACKWARD_AT_LEAST, -1, => [-2,-1]
24+
BACKWARD_AT_MOST, , 5 => [8,9], [6,7]
25+
BACKWARD_AT_MOST, , 6 => [8,9], [6,7]
26+
BACKWARD_AT_MOST, , -1 => [8,9], [6,7], [4,5], [2,3], [0,1]
27+
BACKWARD_CLOSED, 7, 3 => [6,7], [4,5]
28+
BACKWARD_CLOSED, 6, 2 => [6,7], [4,5], [2,3]
29+
BACKWARD_CLOSED, 9, 3 => [8,9], [6,7], [4,5]
30+
BACKWARD_CLOSED, 9, -1 => [8,9], [6,7], [4,5], [2,3], [0,1]
31+
BACKWARD_CLOSED_OPEN, 8, 3 => [8,9], [6,7], [4,5]
32+
BACKWARD_CLOSED_OPEN, 7, 2 => [6,7], [4,5]
33+
BACKWARD_CLOSED_OPEN, 9, 3 => [8,9], [6,7], [4,5]
34+
BACKWARD_CLOSED_OPEN, 9, -1 => [8,9], [6,7], [4,5], [2,3], [0,1]
35+
BACKWARD_GREATER_THAN, 6, => [4,5], [2,3], [0,1], [-2,-1]
36+
BACKWARD_GREATER_THAN, 7, => [6,7], [4,5], [2,3], [0,1], [-2,-1]
37+
BACKWARD_GREATER_THAN, 9, => [8,9], [6,7], [4,5], [2,3], [0,1], [-2,-1]
38+
BACKWARD_GREATER_THAN, -1, => [-2,-1]
39+
BACKWARD_LESS_THAN, , 5 => [8,9], [6,7]
40+
BACKWARD_LESS_THAN, , 2 => [8,9], [6,7], [4,5]
41+
BACKWARD_LESS_THAN, , -1 => [8,9], [6,7], [4,5], [2,3], [0,1]
42+
BACKWARD_OPEN, 7, 2 => [6,7], [4,5]
43+
BACKWARD_OPEN, 8, 1 => [6,7], [4,5], [2,3]
44+
BACKWARD_OPEN, 9, 4 => [8,9], [6,7]
45+
BACKWARD_OPEN, 9, -1 => [8,9], [6,7], [4,5], [2,3], [0,1]
46+
BACKWARD_OPEN_CLOSED, 7, 2 => [6,7], [4,5], [2,3]
47+
BACKWARD_OPEN_CLOSED, 8, 4 => [6,7], [4,5]
48+
BACKWARD_OPEN_CLOSED, 9, 4 => [8,9], [6,7], [4,5]
49+
BACKWARD_OPEN_CLOSED, 9, -1 => [8,9], [6,7], [4,5], [2,3], [0,1]
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
FORWARD_ALL, , => [-2,-1], [0,1], [2,3], [4,5], [6,7], [8,9]
2+
FORWARD_AT_LEAST, 5, => [6,7], [8,9]
3+
FORWARD_AT_LEAST, 6, => [6,7], [8,9]
4+
FORWARD_AT_MOST, , 5 => [-2,-1], [0,1], [2,3], [4,5]
5+
FORWARD_AT_MOST, , 6 => [-2,-1], [0,1], [2,3], [4,5], [6,7]
6+
FORWARD_CLOSED, 3, 7 => [4,5], [6,7]
7+
FORWARD_CLOSED, 2, 6 => [2,3], [4,5], [6,7]
8+
FORWARD_CLOSED, 1, 7 => [2,3], [4,5], [6,7]
9+
FORWARD_CLOSED_OPEN, 3, 8 => [4,5], [6,7]
10+
FORWARD_CLOSED_OPEN, 2, 6 => [2,3], [4,5]
11+
FORWARD_GREATER_THAN, 4, => [6,7], [8,9]
12+
FORWARD_GREATER_THAN, 3, => [4,5], [6,7], [8,9]
13+
FORWARD_LESS_THAN, , 5 => [-2,-1], [0,1], [2,3], [4,5]
14+
FORWARD_LESS_THAN, , 8 => [-2,-1], [0,1], [2,3], [4,5], [6,7]
15+
FORWARD_OPEN, 3, 7 => [4,5], [6,7]
16+
FORWARD_OPEN, 2, 8 => [4,5], [6,7]
17+
FORWARD_OPEN_CLOSED, 3, 8 => [4,5], [6,7], [8,9]
18+
FORWARD_OPEN_CLOSED, 2, 6 => [4,5], [6,7]
19+
BACKWARD_ALL, , => [8,9], [6,7], [4,5], [2,3], [0,1], [-2,-1]
20+
BACKWARD_AT_LEAST, 5, => [4,5], [2,3], [0,1], [-2,-1]
21+
BACKWARD_AT_LEAST, 6, => [6,7], [4,5], [2,3], [0,1], [-2,-1]
22+
BACKWARD_AT_LEAST, 9, => [8,9], [6,7], [4,5], [2,3], [0,1], [-2,-1]
23+
BACKWARD_AT_LEAST, -1, => [-2,-1]
24+
BACKWARD_AT_MOST, , 5 => [8,9], [6,7]
25+
BACKWARD_AT_MOST, , 6 => [8,9], [6,7]
26+
BACKWARD_AT_MOST, , -1 => [8,9], [6,7], [4,5], [2,3], [0,1]
27+
BACKWARD_CLOSED, 7, 3 => [6,7], [4,5]
28+
BACKWARD_CLOSED, 6, 2 => [6,7], [4,5], [2,3]
29+
BACKWARD_CLOSED, 9, 3 => [8,9], [6,7], [4,5]
30+
BACKWARD_CLOSED, 9, -1 => [8,9], [6,7], [4,5], [2,3], [0,1]
31+
BACKWARD_CLOSED_OPEN, 8, 3 => [8,9], [6,7], [4,5]
32+
BACKWARD_CLOSED_OPEN, 7, 2 => [6,7], [4,5]
33+
BACKWARD_CLOSED_OPEN, 9, 3 => [8,9], [6,7], [4,5]
34+
BACKWARD_CLOSED_OPEN, 9, -1 => [8,9], [6,7], [4,5], [2,3], [0,1]
35+
BACKWARD_GREATER_THAN, 6, => [4,5], [2,3], [0,1], [-2,-1]
36+
BACKWARD_GREATER_THAN, 7, => [6,7], [4,5], [2,3], [0,1], [-2,-1]
37+
BACKWARD_GREATER_THAN, 9, => [8,9], [6,7], [4,5], [2,3], [0,1], [-2,-1]
38+
BACKWARD_GREATER_THAN, -1, => [-2,-1]
39+
BACKWARD_LESS_THAN, , 5 => [8,9], [6,7]
40+
BACKWARD_LESS_THAN, , 2 => [8,9], [6,7], [4,5]
41+
BACKWARD_LESS_THAN, , -1 => [8,9], [6,7], [4,5], [2,3], [0,1]
42+
BACKWARD_OPEN, 7, 2 => [6,7], [4,5]
43+
BACKWARD_OPEN, 8, 1 => [6,7], [4,5], [2,3]
44+
BACKWARD_OPEN, 9, 4 => [8,9], [6,7]
45+
BACKWARD_OPEN, 9, -1 => [8,9], [6,7], [4,5], [2,3], [0,1]
46+
BACKWARD_OPEN_CLOSED, 7, 2 => [6,7], [4,5], [2,3]
47+
BACKWARD_OPEN_CLOSED, 8, 4 => [6,7], [4,5]
48+
BACKWARD_OPEN_CLOSED, 9, 4 => [8,9], [6,7], [4,5]
49+
BACKWARD_OPEN_CLOSED, 9, -1 => [8,9], [6,7], [4,5], [2,3], [0,1]

0 commit comments

Comments
 (0)