|
18 | 18 |
|
19 | 19 | import static org.hamcrest.Matchers.equalTo; |
20 | 20 | import static org.hamcrest.Matchers.greaterThanOrEqualTo; |
21 | | -import static org.hamcrest.Matchers.lessThanOrEqualTo; |
22 | 21 |
|
| 22 | +import com.carrotsearch.randomizedtesting.Xoroshiro128PlusRandom; |
| 23 | +import com.carrotsearch.randomizedtesting.generators.RandomNumbers; |
23 | 24 | import java.util.ArrayList; |
24 | 25 | import java.util.Collections; |
| 26 | +import java.util.HashMap; |
25 | 27 | import java.util.Iterator; |
26 | 28 | import java.util.List; |
27 | 29 | import java.util.NoSuchElementException; |
|
30 | 32 | import org.apache.lucene.tests.util.TestUtil; |
31 | 33 | import org.hamcrest.Matchers; |
32 | 34 |
|
33 | | -@SuppressWarnings("BoxedPrimitiveEquality") |
34 | 35 | public class TestPriorityQueue extends LuceneTestCase { |
35 | 36 |
|
36 | 37 | private static class IntegerQueue extends PriorityQueue<Integer> { |
@@ -208,59 +209,48 @@ public void testAddAllDoesNotFitIntoQueue() { |
208 | 209 | () -> pq.addAll(list)); |
209 | 210 | } |
210 | 211 |
|
211 | | - // TODO: incredibly slow |
212 | | - @Nightly |
| 212 | + /** Randomly add and remove elements, comparing against the reference java.util.PriorityQueue. */ |
213 | 213 | public void testRemovalsAndInsertions() { |
214 | | - Random random = random(); |
215 | | - int numDocsInPQ = TestUtil.nextInt(random, 1, 100); |
216 | | - IntegerQueue pq = new IntegerQueue(numDocsInPQ); |
217 | | - Integer lastLeast = null; |
218 | | - |
219 | | - // Basic insertion of new content |
220 | | - ArrayList<Integer> sds = new ArrayList<>(numDocsInPQ); |
221 | | - for (int i = 0; i < numDocsInPQ * 10; i++) { |
222 | | - Integer newEntry = Math.abs(random.nextInt()); |
223 | | - sds.add(newEntry); |
224 | | - Integer evicted = pq.insertWithOverflow(newEntry); |
225 | | - pq.checkValidity(); |
226 | | - if (evicted != null) { |
227 | | - assertTrue(sds.remove(evicted)); |
228 | | - if (evicted != newEntry) { |
229 | | - assertSame(evicted, lastLeast); |
| 214 | + int maxElement = RandomNumbers.randomIntBetween(random(), 1, 10_000); |
| 215 | + int size = maxElement / 2 + 1; |
| 216 | + |
| 217 | + var reference = new java.util.PriorityQueue<Integer>(); |
| 218 | + var pq = new IntegerQueue(size); |
| 219 | + |
| 220 | + Random localRandom = new Xoroshiro128PlusRandom(random().nextLong()); |
| 221 | + |
| 222 | + // Lucene's PriorityQueue.remove uses reference equality, not .equals to determine which |
| 223 | + // elements |
| 224 | + // to remove (!). |
| 225 | + HashMap<Integer, Integer> ints = new HashMap<>(); |
| 226 | + |
| 227 | + for (int i = 0, iters = size * 2; i < iters; i++) { |
| 228 | + Integer element = ints.computeIfAbsent(localRandom.nextInt(maxElement), k -> k); |
| 229 | + |
| 230 | + var action = localRandom.nextInt(100); |
| 231 | + if (action < 25) { |
| 232 | + // removals, possibly misses. |
| 233 | + assertEquals("remove() difference: " + i, reference.remove(element), pq.remove(element)); |
| 234 | + } else { |
| 235 | + // additions. |
| 236 | + var dropped = pq.insertWithOverflow(element); |
| 237 | + |
| 238 | + reference.add(element); |
| 239 | + Integer droppedReference; |
| 240 | + if (reference.size() > size) { |
| 241 | + droppedReference = reference.remove(); |
| 242 | + } else { |
| 243 | + droppedReference = null; |
230 | 244 | } |
231 | | - } |
232 | | - Integer newLeast = pq.top(); |
233 | | - if ((lastLeast != null) && (newLeast != newEntry) && (newLeast != lastLeast)) { |
234 | | - // If there has been a change of least entry and it wasn't our new |
235 | | - // addition we expect the scores to increase |
236 | | - assertThat(newLeast, lessThanOrEqualTo(newEntry)); |
237 | | - assertThat(newLeast, greaterThanOrEqualTo(lastLeast)); |
238 | | - } |
239 | | - lastLeast = newLeast; |
240 | | - } |
241 | 245 |
|
242 | | - // Try many random additions to existing entries - we should always see |
243 | | - // increasing scores in the lowest entry in the PQ |
244 | | - for (int p = 0; p < 500000; p++) { |
245 | | - int element = (int) (random.nextFloat() * (sds.size() - 1)); |
246 | | - Integer objectToRemove = sds.get(element); |
247 | | - assertSame(sds.remove(element), objectToRemove); |
248 | | - assertTrue(pq.remove(objectToRemove)); |
249 | | - pq.checkValidity(); |
250 | | - Integer newEntry = Math.abs(random.nextInt()); |
251 | | - sds.add(newEntry); |
252 | | - assertNull(pq.insertWithOverflow(newEntry)); |
253 | | - pq.checkValidity(); |
254 | | - Integer newLeast = pq.top(); |
255 | | - if ((objectToRemove != lastLeast) && (lastLeast != null) && (newLeast != newEntry)) { |
256 | | - // If there has been a change of least entry and it wasn't our new |
257 | | - // addition or the loss of our randomly removed entry we expect the |
258 | | - // scores to increase |
259 | | - assertThat(newLeast, lessThanOrEqualTo(newEntry)); |
260 | | - assertThat(newLeast, greaterThanOrEqualTo(lastLeast)); |
| 246 | + assertEquals("insertWithOverflow() difference.", dropped, droppedReference); |
261 | 247 | } |
262 | | - lastLeast = newLeast; |
| 248 | + |
| 249 | + assertEquals("insertWithOverflow() size difference?", reference.size(), pq.size()); |
| 250 | + assertEquals("top() difference?", reference.peek(), pq.top()); |
263 | 251 | } |
| 252 | + |
| 253 | + pq.checkValidity(); |
264 | 254 | } |
265 | 255 |
|
266 | 256 | public void testIteratorEmpty() { |
|
0 commit comments