<commit_before>#include "scheduler.h"
#include "../task/taskexecutor.h"
#include "../log/logger.h"

#include <QDir>
#include <QTimer>
#include <QCoreApplication>
#include <QDebug>
#include <QPointer>

LOGGER(Scheduler);

class Scheduler::Private : public QObject
{
    Q_OBJECT

public:
    Private(Scheduler* q)
    : q(q)
    , path(qApp->applicationDirPath())
    {
        timer.setSingleShot(true);
        connect(&timer, SIGNAL(timeout()), this, SLOT(timeout()));
    }

    Scheduler* q;

    // Properties
    QDir path;
    QTimer timer;
    TestDefinitionList tests;

    QPointer<TaskExecutor> executor;

    // Functions
    void updateTimer();
    int enqueue(const TestDefinitionPtr &testDefinition);

public slots:
    void timeout();
};

void Scheduler::Private::updateTimer()
{
    if (tests.isEmpty()) {
        timer.stop();
        LOG_DEBUG("Scheduling timer stopped");
    } else {
        const TestDefinitionPtr td = tests.at(0);
        int ms = td->timing()->timeLeft();
        if ( ms > 0 )
            timer.start( ms );
        else {
            // If we would call timeout() directly, the testAdded() signal
            // would be emitted after execution.
            timer.start(1);
        }

        LOG_DEBUG(QString("Scheduling timer executes %1 in %2 ms").arg(td->name()).arg(ms));
    }
}

int Scheduler::Private::enqueue(const TestDefinitionPtr& testDefinition)
{
    bool wasEmpty = tests.isEmpty();

    int timeLeft = testDefinition->timing()->timeLeft();

    for(int i=0; i < tests.size(); ++i) {
        const TestDefinitionPtr& td = tests.at(i);
        if (timeLeft < td->timing()->timeLeft()) {
            tests.insert(i, testDefinition);

            if ( wasEmpty || i == 0 )
                updateTimer();

            return i;
        }
    }

    tests.append(testDefinition);

    if ( wasEmpty )
        updateTimer();

    return tests.size()-1;
}

void Scheduler::Private::timeout()
{
    for(int i=0; i < tests.size(); ++i) {
        TestDefinitionPtr td = tests.at(i);

        // We assume they are already sorted - WRONG, SO WRONG!
        if ( td->timing()->timeLeft() > 0 )
            continue;

        q->execute(td);

        tests.removeAt(i);

        if (!td->timing()->reset()) {
            emit q->testRemoved(td, i);

            if (tests.isEmpty())<commit_msg>Fixes #19: Duplicate Tasks are not sorted out by scheduler<commit_after>#include "scheduler.h"
#include "../task/taskexecutor.h"
#include "../log/logger.h"

#include <QDir>
#include <QTimer>
#include <QCoreApplication>
#include <QDebug>
#include <QPointer>
#include <QSet>

LOGGER(Scheduler);

class Scheduler::Private : public QObject
{
    Q_OBJECT

public:
    Private(Scheduler* q)
    : q(q)
    , path(qApp->applicationDirPath())
    {
        timer.setSingleShot(true);
        connect(&timer, SIGNAL(timeout()), this, SLOT(timeout()));
    }

    Scheduler* q;

    // Properties
    QDir path;
    QTimer timer;

    TestDefinitionList tests;
    QSet<QUuid> testIds;

    QPointer<TaskExecutor> executor;

    // Functions
    void updateTimer();
    int enqueue(const TestDefinitionPtr &testDefinition);

public slots:
    void timeout();
};

void Scheduler::Private::updateTimer()
{
    if (tests.isEmpty()) {
        timer.stop();
        LOG_DEBUG("Scheduling timer stopped");
    } else {
        const TestDefinitionPtr td = tests.at(0);
        int ms = td->timing()->timeLeft();
        if ( ms > 0 )
            timer.start( ms );
        else {
            // If we would call timeout() directly, the testAdded() signal
            // would be emitted after execution.
            timer.start(1);
        }

        LOG_DEBUG(QString("Scheduling timer executes %1 in %2 ms").arg(td->name()).arg(ms));
    }
}

int Scheduler::Private::enqueue(const TestDefinitionPtr& testDefinition)
{
    if ( testIds.contains(testDefinition->id()) )
        return -1;

    bool wasEmpty = tests.isEmpty();

    int timeLeft = testDefinition->timing()->timeLeft();

    for(int i=0; i < tests.size(); ++i) {
        const TestDefinitionPtr& td = tests.at(i);
        if (timeLeft < td->timing()->timeLeft()) {
            tests.insert(i, testDefinition);
            testIds.insert(testDefinition->id());

            if ( wasEmpty || i == 0 )
                updateTimer();

            return i;
        }
    }

    tests.append(testDefinition);

    if ( wasEmpty )
        updateTimer();

    return tests.size()-1;
}

void Scheduler::Private::timeout()
{
    for(int i=0; i < tests.size(); ++i) {
        TestDefinitionPtr td = tests.at(i);

        // We assume they are already sorted - WRONG, SO WRONG!
        if ( td->timing()->timeLeft() > 0 )
            continue;

        q->execute(td);

        tests.removeAt(i);

        if (!td->timing()->reset()) {
            emit q->testRemoved(td, i);
            testIds.remove(td->id());

            if (tests.isEmpty())
{'fraction_non_alphanumeric': 0.1475542926730262, 'fraction_numerical': 0.004668155063933428, 'mean_word_length': 2.3364928909952605, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 52, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5092443', 'n_tokens_mistral': 1549, 'n_tokens_neox': 1493, 'n_words': 352}