mirror of
https://github.com/YACReader/yacreader
synced 2025-06-04 01:28:55 -04:00
Make ConcurrentQueueTest::cancelPending1UserThread() non-flaky
ConcurrentQueueTest::cancelPending1UserThread() often fails when ConcurrentQueueTest is launched from Qt Creator immediately after switching between Debug and Release YACReader build configurations. The CPU busyness must be affecting the thread scheduling timing, which breaks the test's timing assumptions in this case. Use the return value of ConcurrentQueue::cancelPending() instead of relying on the timing of thread scheduling to determine the number of canceled jobs.
This commit is contained in:
parent
b43e3383d5
commit
34b0698d02
@ -47,14 +47,27 @@ struct JobData {
|
|||||||
};
|
};
|
||||||
using JobDataSet = QVector<JobData>;
|
using JobDataSet = QVector<JobData>;
|
||||||
|
|
||||||
int expectedTotal(const JobDataSet &jobs)
|
int expectedTotal(JobDataSet::const_iterator first, JobDataSet::const_iterator last)
|
||||||
{
|
{
|
||||||
return std::accumulate(jobs.cbegin(), jobs.cend(), 0,
|
return std::accumulate(first, last, 0,
|
||||||
[](int total, JobData job) {
|
[](int total, JobData job) {
|
||||||
return total + job.summand;
|
return total + job.summand;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int expectedTotal(const JobDataSet &jobs)
|
||||||
|
{
|
||||||
|
return expectedTotal(jobs.cbegin(), jobs.cend());
|
||||||
|
}
|
||||||
|
|
||||||
|
int expectedTotal(const JobDataSet &jobs, std::size_t canceledCount)
|
||||||
|
{
|
||||||
|
const auto count = jobs.size() - static_cast<int>(canceledCount);
|
||||||
|
if (count < 0)
|
||||||
|
qFatal("Canceled more than the total number of jobs somehow!");
|
||||||
|
return expectedTotal(jobs.cbegin(), jobs.cbegin() + count);
|
||||||
|
}
|
||||||
|
|
||||||
int expectedTotal(const QVector<JobDataSet> &jobs)
|
int expectedTotal(const QVector<JobDataSet> &jobs)
|
||||||
{
|
{
|
||||||
return std::accumulate(jobs.cbegin(), jobs.cend(), 0,
|
return std::accumulate(jobs.cbegin(), jobs.cend(), 0,
|
||||||
@ -134,23 +147,30 @@ public:
|
|||||||
|
|
||||||
void printStartedMessage() const
|
void printStartedMessage() const
|
||||||
{
|
{
|
||||||
log() << messageFormatString().arg("started");
|
log() << threadMessageFormatString().arg("started");
|
||||||
}
|
}
|
||||||
void printCanceledMessage() const
|
void printCanceledMessage(std::size_t canceledCount) const
|
||||||
{
|
{
|
||||||
log() << messageFormatString().arg("canceled");
|
const char *const jobStr = canceledCount == 1 ? "job" : "jobs";
|
||||||
|
const auto format = messageFormatString().arg("%1 %2 %3");
|
||||||
|
log() << format.arg("canceled").arg(canceledCount).arg(jobStr);
|
||||||
}
|
}
|
||||||
void printWaitedMessage() const
|
void printWaitedMessage() const
|
||||||
{
|
{
|
||||||
log() << messageFormatString().arg("waited for");
|
log() << threadMessageFormatString().arg("waited for");
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString messageFormatString() const
|
QString messageFormatString() const
|
||||||
{
|
{
|
||||||
auto format = QStringLiteral("#%1 %5 %2 %3 => %4").arg(threadId);
|
return QStringLiteral("#%1 %3 => %2").arg(threadId).arg(total.load());
|
||||||
|
}
|
||||||
|
|
||||||
|
QString threadMessageFormatString() const
|
||||||
|
{
|
||||||
const char *const threadStr = threadCount == 1 ? "thread" : "threads";
|
const char *const threadStr = threadCount == 1 ? "thread" : "threads";
|
||||||
return format.arg(threadCount).arg(threadStr).arg(total.load());
|
const auto format = messageFormatString().arg("%3 %1 %2");
|
||||||
|
return format.arg(threadCount).arg(threadStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
const Total &total;
|
const Total &total;
|
||||||
@ -158,6 +178,13 @@ private:
|
|||||||
const int threadCount;
|
const int threadCount;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::size_t cancelAndPrint(ConcurrentQueue &queue, const QueueControlMessagePrinter &printer)
|
||||||
|
{
|
||||||
|
const auto canceledCount = queue.cancelPending();
|
||||||
|
printer.printCanceledMessage(canceledCount);
|
||||||
|
return canceledCount;
|
||||||
|
}
|
||||||
|
|
||||||
void waitAndPrint(ConcurrentQueue &queue, const QueueControlMessagePrinter &printer)
|
void waitAndPrint(ConcurrentQueue &queue, const QueueControlMessagePrinter &printer)
|
||||||
{
|
{
|
||||||
queue.waitAll();
|
queue.waitAll();
|
||||||
@ -301,39 +328,38 @@ void ConcurrentQueueTest::cancelPending1UserThread_data()
|
|||||||
QTest::addColumn<int>("threadCount");
|
QTest::addColumn<int>("threadCount");
|
||||||
QTest::addColumn<JobDataSet>("jobs");
|
QTest::addColumn<JobDataSet>("jobs");
|
||||||
QTest::addColumn<Clock::duration>("cancelDelay");
|
QTest::addColumn<Clock::duration>("cancelDelay");
|
||||||
QTest::addColumn<int>("expectedTotal");
|
|
||||||
|
|
||||||
const auto ms = [](int count) -> Clock::duration { return chrono::milliseconds(count); };
|
const auto ms = [](int count) -> Clock::duration { return chrono::milliseconds(count); };
|
||||||
const auto us = [](int count) -> Clock::duration { return chrono::microseconds(count); };
|
const auto us = [](int count) -> Clock::duration { return chrono::microseconds(count); };
|
||||||
|
|
||||||
QTest::newRow("-") << 0 << JobDataSet {} << ms(0) << 0;
|
QTest::newRow("-") << 0 << JobDataSet {} << ms(0);
|
||||||
QTest::newRow("01") << 2 << JobDataSet {} << ms(0) << 0;
|
QTest::newRow("01") << 2 << JobDataSet {} << ms(0);
|
||||||
QTest::newRow("02") << 3 << JobDataSet {} << ms(1) << 0;
|
QTest::newRow("02") << 3 << JobDataSet {} << ms(1);
|
||||||
QTest::newRow("A") << 1 << JobDataSet { { 5, ms(3) } } << ms(1) << 5;
|
QTest::newRow("A") << 1 << JobDataSet { { 5, ms(3) } } << ms(1);
|
||||||
QTest::newRow("B") << 5 << JobDataSet { { 12, ms(1) } } << ms(1) << 12;
|
QTest::newRow("B") << 5 << JobDataSet { { 12, ms(1) } } << ms(1);
|
||||||
|
|
||||||
JobDataSet dataSet { { 1, ms(3) }, { 5, ms(2) }, { 3, ms(1) } };
|
JobDataSet dataSet { { 1, ms(3) }, { 5, ms(2) }, { 3, ms(1) } };
|
||||||
QTest::newRow("C1") << 1 << dataSet << ms(1) << 1;
|
QTest::newRow("C1") << 1 << dataSet << ms(1);
|
||||||
QTest::newRow("C2") << 1 << dataSet << ms(4) << 6;
|
QTest::newRow("C2") << 1 << dataSet << ms(4);
|
||||||
QTest::newRow("C3") << 2 << dataSet << ms(1) << 6;
|
QTest::newRow("C3") << 2 << dataSet << ms(1);
|
||||||
QTest::newRow("C4") << 3 << dataSet << ms(1) << 9;
|
QTest::newRow("C4") << 3 << dataSet << ms(1);
|
||||||
QTest::newRow("C5") << 1 << dataSet << ms(7) << 9;
|
QTest::newRow("C5") << 1 << dataSet << ms(7);
|
||||||
|
|
||||||
dataSet.push_back({ 10, ms(5) });
|
dataSet.push_back({ 10, ms(5) });
|
||||||
dataSet.push_back({ 20, ms(8) });
|
dataSet.push_back({ 20, ms(8) });
|
||||||
dataSet.push_back({ 40, ms(20) });
|
dataSet.push_back({ 40, ms(20) });
|
||||||
dataSet.push_back({ 80, ms(2) });
|
dataSet.push_back({ 80, ms(2) });
|
||||||
QTest::newRow("D1") << 1 << dataSet << ms(1) << 1;
|
QTest::newRow("D1") << 1 << dataSet << ms(1);
|
||||||
QTest::newRow("D2") << 1 << dataSet << ms(15) << 39;
|
QTest::newRow("D2") << 1 << dataSet << ms(15);
|
||||||
QTest::newRow("D3") << 1 << dataSet << ms(50) << 159;
|
QTest::newRow("D3") << 1 << dataSet << ms(50);
|
||||||
QTest::newRow("D4") << 2 << dataSet << ms(4) << 39;
|
QTest::newRow("D4") << 2 << dataSet << ms(4);
|
||||||
QTest::newRow("D5") << 3 << dataSet << ms(4) << 79;
|
QTest::newRow("D5") << 3 << dataSet << ms(4);
|
||||||
QTest::newRow("D6") << 4 << dataSet << ms(4) << 159;
|
QTest::newRow("D6") << 4 << dataSet << ms(4);
|
||||||
QTest::newRow("D7") << 2 << dataSet << us(300) << 6;
|
QTest::newRow("D7") << 2 << dataSet << us(300);
|
||||||
QTest::newRow("D8") << 3 << dataSet << us(500) << 9;
|
QTest::newRow("D8") << 3 << dataSet << us(500);
|
||||||
QTest::newRow("D9") << 4 << dataSet << us(700) << 19;
|
QTest::newRow("D9") << 4 << dataSet << us(700);
|
||||||
|
|
||||||
QTest::newRow("E") << 4 << JobDataSet { { 20, ms(1) }, { 8, ms(5) }, { 5, ms(2) } } << ms(1) << 33;
|
QTest::newRow("E") << 4 << JobDataSet { { 20, ms(1) }, { 8, ms(5) }, { 5, ms(2) } } << ms(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConcurrentQueueTest::cancelPending1UserThread()
|
void ConcurrentQueueTest::cancelPending1UserThread()
|
||||||
@ -341,7 +367,6 @@ void ConcurrentQueueTest::cancelPending1UserThread()
|
|||||||
QFETCH(const int, threadCount);
|
QFETCH(const int, threadCount);
|
||||||
QFETCH(const JobDataSet, jobs);
|
QFETCH(const JobDataSet, jobs);
|
||||||
QFETCH(const Clock::duration, cancelDelay);
|
QFETCH(const Clock::duration, cancelDelay);
|
||||||
QFETCH(const int, expectedTotal);
|
|
||||||
|
|
||||||
const auto printer = makeMessagePrinter(threadCount);
|
const auto printer = makeMessagePrinter(threadCount);
|
||||||
|
|
||||||
@ -351,12 +376,12 @@ void ConcurrentQueueTest::cancelPending1UserThread()
|
|||||||
Enqueuer(queue, total, jobs, primaryThreadId)();
|
Enqueuer(queue, total, jobs, primaryThreadId)();
|
||||||
|
|
||||||
std::this_thread::sleep_for(cancelDelay);
|
std::this_thread::sleep_for(cancelDelay);
|
||||||
queue.cancelPending();
|
const auto canceledCount = cancelAndPrint(queue, printer);
|
||||||
printer.printCanceledMessage();
|
QVERIFY(canceledCount <= static_cast<std::size_t>(jobs.size()));
|
||||||
|
|
||||||
waitAndPrint(queue, printer);
|
waitAndPrint(queue, printer);
|
||||||
|
|
||||||
QCOMPARE(total.load(), expectedTotal);
|
QCOMPARE(total.load(), expectedTotal(jobs, canceledCount));
|
||||||
}
|
}
|
||||||
|
|
||||||
QTEST_APPLESS_MAIN(ConcurrentQueueTest)
|
QTEST_APPLESS_MAIN(ConcurrentQueueTest)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user