/*
* Copyright 2015 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkTDPQueue.h"
#include "SkRandom.h"
#include "Test.h"
namespace { bool intless(const int& a, const int& b) { return a < b; } }
static void simple_test(skiatest::Reporter* reporter) {
SkTDPQueue<int, intless> heap;
REPORTER_ASSERT(reporter, 0 == heap.count());
heap.insert(0);
REPORTER_ASSERT(reporter, 1 == heap.count());
REPORTER_ASSERT(reporter, 0 == heap.peek());
heap.pop();
REPORTER_ASSERT(reporter, 0 == heap.count());
heap.insert(0);
heap.insert(1);
REPORTER_ASSERT(reporter, 2 == heap.count());
REPORTER_ASSERT(reporter, 0 == heap.peek());
heap.pop();
REPORTER_ASSERT(reporter, 1 == heap.count());
REPORTER_ASSERT(reporter, 1 == heap.peek());
heap.pop();
REPORTER_ASSERT(reporter, 0 == heap.count());
heap.insert(2);
heap.insert(1);
heap.insert(0);
REPORTER_ASSERT(reporter, 3 == heap.count());
REPORTER_ASSERT(reporter, 0 == heap.peek());
heap.pop();
REPORTER_ASSERT(reporter, 2 == heap.count());
REPORTER_ASSERT(reporter, 1 == heap.peek());
heap.pop();
REPORTER_ASSERT(reporter, 1 == heap.count());
REPORTER_ASSERT(reporter, 2 == heap.peek());
heap.pop();
REPORTER_ASSERT(reporter, 0 == heap.count());
heap.insert(2);
heap.insert(3);
heap.insert(0);
heap.insert(1);
REPORTER_ASSERT(reporter, 4 == heap.count());
REPORTER_ASSERT(reporter, 0 == heap.peek());
heap.pop();
REPORTER_ASSERT(reporter, 3 == heap.count());
REPORTER_ASSERT(reporter, 1 == heap.peek());
heap.pop();
REPORTER_ASSERT(reporter, 2 == heap.count());
REPORTER_ASSERT(reporter, 2 == heap.peek());
heap.pop();
REPORTER_ASSERT(reporter, 1 == heap.count());
REPORTER_ASSERT(reporter, 3 == heap.peek());
heap.pop();
REPORTER_ASSERT(reporter, 0 == heap.count());
}
struct Dummy {
int fValue;
int fPriority;
mutable int fIndex;
static bool LessP(Dummy* const& a, Dummy* const& b) { return a->fPriority < b->fPriority; }
static int* PQIndex(Dummy* const& dummy) { return &dummy->fIndex; }
bool operator== (const Dummy& that) const {
return fValue == that.fValue && fPriority == that.fPriority;
}
bool operator!= (const Dummy& that) const { return !(*this == that); }
};
void random_test(skiatest::Reporter* reporter) {
SkRandom random;
static const Dummy kSentinel = {-1, -1, -1};
for (int i = 0; i < 100; ++i) {
// Create a random set of Dummy objects.
int count = random.nextULessThan(100);
SkTDArray<Dummy> array;
array.setReserve(count);
for (int j = 0; j < count; ++j) {
Dummy* dummy = array.append();
dummy->fPriority = random.nextS();
dummy->fValue = random.nextS();
dummy->fIndex = -1;
if (*dummy == kSentinel) {
array.pop();
--j;
}
}
// Stick the dummy objects in the pqueue.
SkTDPQueue<Dummy*, Dummy::LessP, Dummy::PQIndex> pq;
for (int j = 0; j < count; ++j) {
pq.insert(&array[j]);
}
REPORTER_ASSERT(reporter, pq.count() == array.count());
for (int j = 0; j < count; ++j) {
// every item should have an entry in the queue.
REPORTER_ASSERT(reporter, -1 != array[j].fIndex);
}
// Begin the test.
while (pq.count()) {
// Make sure the top of the queue is really the highest priority.
Dummy* top = pq.peek();
for (int k = 0; k < count; ++k) {
REPORTER_ASSERT(reporter, kSentinel == array[k] ||
array[k].fPriority >= top->fPriority);
}
// Do one of three random actions:
unsigned action = random.nextULessThan(3);
switch (action) {
case 0: { // pop the top,
Dummy* top = pq.peek();
REPORTER_ASSERT(reporter, array.begin() <= top && top < array.end());
pq.pop();
*top = kSentinel;
break;
}
case 1: { // remove a random element,
int item;
do {
item = random.nextULessThan(count);
} while (array[item] == kSentinel);
pq.remove(&array[item]);
array[item] = kSentinel;
break;
}
case 2: { // or change an element's priority.
int item;
do {
item = random.nextULessThan(count);
} while (array[item] == kSentinel);
array[item].fPriority = random.nextS();
pq.priorityDidChange(&array[item]);
break;
}
}
}
}
}
void sort_test(skiatest::Reporter* reporter) {
SkRandom random;
SkTDPQueue<Dummy *, Dummy::LessP, Dummy::PQIndex> pqTest;
SkTDPQueue<Dummy *, Dummy::LessP, Dummy::PQIndex> pqControl;
// Create a random set of Dummy objects and populate the test queue.
int count = random.nextULessThan(100);
SkTDArray<Dummy> testArray;
testArray.setReserve(count);
for (int i = 0; i < count; i++) {
Dummy *dummy = testArray.append();
dummy->fPriority = random.nextS();
dummy->fValue = random.nextS();
dummy->fIndex = -1;
pqTest.insert(&testArray[i]);
}
// Stick equivalent dummy objects into the control queue.
SkTDArray<Dummy> controlArray;
controlArray.setReserve(count);
for (int i = 0; i < count; i++) {
Dummy *dummy = controlArray.append();
dummy->fPriority = testArray[i].fPriority;
dummy->fValue = testArray[i].fValue;
dummy->fIndex = -1;
pqControl.insert(&controlArray[i]);
}
// Sort the queue
pqTest.sort();
// Compare elements in the queue to ensure they are in sorted order
int prevPriority = pqTest.peek()->fPriority;
for (int i = 0; i < count; i++) {
REPORTER_ASSERT(reporter, i <= pqTest.at(i)->fIndex);
REPORTER_ASSERT(reporter, prevPriority <= pqTest.at(i)->fPriority);
prevPriority = pqTest.at(i)->fPriority;
}
// Verify that after sorting the queue still produces the same result as the control queue
for (int i = 0; i < count; i++) {
REPORTER_ASSERT(reporter, *pqControl.peek() == *pqTest.peek());
pqControl.pop();
pqTest.pop();
}
}
DEF_TEST(TDPQueueTest, reporter) {
simple_test(reporter);
random_test(reporter);
sort_test(reporter);
}