view data/model/test/TestSparseModels.h @ 1784:4eac4bf35b45

More graceful handling of failure to construct FFT models in the case where the source model has already been deleted before this occurs
author Chris Cannam
date Tue, 17 Sep 2019 11:21:33 +0100
parents 9d82b164f264
children
line wrap: on
line source
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */

/*
    Sonic Visualiser
    An audio file viewer and annotation editor.
    Centre for Digital Music, Queen Mary, University of London.
    
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
    published by the Free Software Foundation; either version 2 of the
    License, or (at your option) any later version.  See the file
    COPYING included with this distribution for more information.
*/

#ifndef TEST_SPARSE_MODELS_H
#define TEST_SPARSE_MODELS_H

#include "../SparseOneDimensionalModel.h"
#include "../NoteModel.h"
#include "../TextModel.h"
#include "../Path.h"
#include "../ImageModel.h"

#include <QObject>
#include <QtTest>

#include <iostream>

using namespace std;

// NB model & dataset IDs in the export tests are incremental,
// depending on how many have been exported in previous tests - so
// when adding or removing tests we may occasionally need to update
// the IDs in other ones

class TestSparseModels : public QObject
{
    Q_OBJECT

private slots:
    void s1d_empty() {
        SparseOneDimensionalModel m(100, 10, false);
        QCOMPARE(m.isEmpty(), true);
        QCOMPARE(m.getEventCount(), 0);
        QCOMPARE(m.getAllEvents().size(), size_t(0));
        QCOMPARE(m.getStartFrame(), sv_frame_t(0));
        QCOMPARE(m.getEndFrame(), sv_frame_t(0));
        QCOMPARE(m.getSampleRate(), 100.0);
        QCOMPARE(m.getResolution(), 10);
        QCOMPARE(m.isSparse(), true);

        Event p(10);
        m.add(p);
        m.remove(p);
        QCOMPARE(m.isEmpty(), true);
        QCOMPARE(m.getEventCount(), 0);
        QCOMPARE(m.getAllEvents().size(), size_t(0));
        QCOMPARE(m.getStartFrame(), sv_frame_t(0));
        QCOMPARE(m.getEndFrame(), sv_frame_t(0));
    }

    void s1d_extents() {
        SparseOneDimensionalModel m(100, 10, false);
        Event p1(20);
        m.add(p1);
        QCOMPARE(m.isEmpty(), false);
        QCOMPARE(m.getEventCount(), 1);
        Event p2(50);
        m.add(p2);
        QCOMPARE(m.isEmpty(), false);
        QCOMPARE(m.getEventCount(), 2);
        QCOMPARE(m.getAllEvents().size(), size_t(2));
        QCOMPARE(*m.getAllEvents().begin(), p1);
        QCOMPARE(*m.getAllEvents().rbegin(), p2);
        QCOMPARE(m.getStartFrame(), sv_frame_t(20));
        QCOMPARE(m.getEndFrame(), sv_frame_t(60));
        QCOMPARE(m.containsEvent(p1), true);
        m.remove(p1);
        QCOMPARE(m.getEventCount(), 1);
        QCOMPARE(m.getAllEvents().size(), size_t(1));
        QCOMPARE(*m.getAllEvents().begin(), p2);
        QCOMPARE(m.getStartFrame(), sv_frame_t(50));
        QCOMPARE(m.getEndFrame(), sv_frame_t(60));
        QCOMPARE(m.containsEvent(p1), false);
    }
             
    void s1d_sample() {
        SparseOneDimensionalModel m(100, 10, false);
        Event p1(20), p2(20), p3(50);
        m.add(p1);
        m.add(p2);
        m.add(p3);
        QCOMPARE(m.getAllEvents().size(), size_t(3));
        QCOMPARE(*m.getAllEvents().begin(), p1);
        QCOMPARE(*m.getAllEvents().rbegin(), p3);

        // The EventSeries that is used internally is tested more
        // thoroughly in its own test suite. This is just a check
        auto pp = m.getEventsWithin(20, 10);
        QCOMPARE(pp.size(), size_t(2));
        QCOMPARE(*pp.begin(), p1);
        QCOMPARE(*pp.rbegin(), p2);
        
        pp = m.getEventsWithin(40, 10);
        QCOMPARE(pp.size(), size_t(0));

        pp = m.getEventsStartingAt(50);
        QCOMPARE(pp.size(), size_t(1));
        QCOMPARE(*pp.begin(), p3);
    }

    void s1d_xml() {
        SparseOneDimensionalModel m(100, 10, false);
        m.setObjectName("This \"&\" that");
        Event p1(20);
        Event p2(20, "Label &'\">");
        Event p3(50, 12.4f, 16, ""); // value + duration should not be saved
        m.add(p1);
        m.add(p2);
        m.add(p3);
        QString xml;
        QTextStream str(&xml, QIODevice::WriteOnly);
        m.toXml(str);
        str.flush();
        QString expected =
            "<model id='1' name='This &quot;&amp;&quot; that' sampleRate='100' start='20' end='60' type='sparse' dimensions='1' resolution='10' notifyOnAdd='true' dataset='0' />\n"
            "<dataset id='0' dimensions='1'>\n"
            "  <point frame='20' label='' />\n"
            "  <point frame='20' label='Label &amp;&apos;&quot;&gt;' />\n"
            "  <point frame='50' label='' />\n"
            "</dataset>\n";
        expected.replace("\'", "\"");
        if (xml != expected) {
            cerr << "Obtained xml:\n" << xml
                 << "\nExpected:\n" << expected << std::endl;
        }
        QCOMPARE(xml, expected);
    }

    void note_extents() {
        NoteModel m(100, 10, false);
        Event p1(20, 123.4f, 40, 0.8f, "note 1");
        m.add(p1);
        QCOMPARE(m.isEmpty(), false);
        QCOMPARE(m.getEventCount(), 1);
        Event p2(50, 124.3f, 30, 0.9f, "note 2");
        m.add(p2);
        QCOMPARE(m.isEmpty(), false);
        QCOMPARE(m.getEventCount(), 2);
        QCOMPARE(m.getAllEvents().size(), size_t(2));
        QCOMPARE(*m.getAllEvents().begin(), p1);
        QCOMPARE(*m.getAllEvents().rbegin(), p2);
        QCOMPARE(m.getStartFrame(), sv_frame_t(20));
        QCOMPARE(m.getEndFrame(), sv_frame_t(80));
        QCOMPARE(m.containsEvent(p1), true);
        QCOMPARE(m.getValueMinimum(), 123.4f);
        QCOMPARE(m.getValueMaximum(), 124.3f);
        m.remove(p1);
        QCOMPARE(m.getEventCount(), 1);
        QCOMPARE(m.getAllEvents().size(), size_t(1));
        QCOMPARE(*m.getAllEvents().begin(), p2);
        QCOMPARE(m.getStartFrame(), sv_frame_t(50));
        QCOMPARE(m.getEndFrame(), sv_frame_t(80));
        QCOMPARE(m.containsEvent(p1), false);
    }
             
    void note_sample() {
        NoteModel m(100, 10, false);
        Event p1(20, 123.4f, 10, 0.8f, "note 1");
        Event p2(20, 124.3f, 20, 0.9f, "note 2");
        Event p3(50, 126.3f, 30, 0.9f, "note 3");
        m.add(p1);
        m.add(p2);
        m.add(p3);

        QCOMPARE(m.getAllEvents().size(), size_t(3));
        QCOMPARE(*m.getAllEvents().begin(), p1);
        QCOMPARE(*m.getAllEvents().rbegin(), p3);

        auto pp = m.getEventsSpanning(20, 10);
        QCOMPARE(pp.size(), size_t(2));
        QCOMPARE(*pp.begin(), p1);
        QCOMPARE(*pp.rbegin(), p2);

        pp = m.getEventsSpanning(30, 20);
        QCOMPARE(pp.size(), size_t(1));
        QCOMPARE(*pp.begin(), p2);

        pp = m.getEventsSpanning(40, 10);
        QCOMPARE(pp.size(), size_t(0));

        pp = m.getEventsCovering(50);
        QCOMPARE(pp.size(), size_t(1));
        QCOMPARE(*pp.begin(), p3);
    }

    void note_xml() {
        NoteModel m(100, 10, false);
        Event p1(20, 123.4f, 20, 0.8f, "note 1");
        Event p2(20, 124.3f, 10, 0.9f, "note 2");
        Event p3(50, 126.3f, 30, 0.9f, "note 3");
        m.setScaleUnits("Hz");
        m.add(p1);
        m.add(p2);
        m.add(p3);
        QString xml;
        QTextStream str(&xml, QIODevice::WriteOnly);
        m.toXml(str);
        str.flush();
        
        QString expected =
            "<model id='3' name='' sampleRate='100' start='20' end='80' type='sparse' dimensions='3' resolution='10' notifyOnAdd='true' dataset='2' subtype='note' valueQuantization='0' minimum='123.4' maximum='126.3' units='Hz' />\n"
            "<dataset id='2' dimensions='3'>\n"
            "  <point frame='20' value='124.3' duration='10' level='0.9' label='note 2' />\n"
            "  <point frame='20' value='123.4' duration='20' level='0.8' label='note 1' />\n"
            "  <point frame='50' value='126.3' duration='30' level='0.9' label='note 3' />\n"
            "</dataset>\n";
        expected.replace("\'", "\"");
        if (xml != expected) {
            cerr << "Obtained xml:\n" << xml
                 << "\nExpected:\n" << expected << std::endl;
        }
        QCOMPARE(xml, expected);
    }

    void text_xml() {
        TextModel m(100, 10, false);
        Event p1(20, 1.0f, "text 1");
        Event p2(20, 0.0f, "text 2");
        Event p3(50, 0.3f, "text 3");
        m.add(p1);
        m.add(p2.withLevel(0.8f));
        m.add(p3);
        QString xml;
        QTextStream str(&xml, QIODevice::WriteOnly);
        m.toXml(str);
        str.flush();

        QString expected =
            "<model id='5' name='' sampleRate='100' start='20' end='60' type='sparse' dimensions='2' resolution='10' notifyOnAdd='true' dataset='4' subtype='text' />\n"
            "<dataset id='4' dimensions='2'>\n"
            "  <point frame='20' height='0' label='text 2' />\n"
            "  <point frame='20' height='1' label='text 1' />\n"
            "  <point frame='50' height='0.3' label='text 3' />\n"
            "</dataset>\n";
        expected.replace("\'", "\"");
        if (xml != expected) {
            cerr << "Obtained xml:\n" << xml
                 << "\nExpected:\n" << expected << std::endl;
        }
        QCOMPARE(xml, expected);
    }
    
    void path_xml() {
        Path m(100, 10);
        PathPoint p1(20, 30);
        PathPoint p2(40, 60);
        PathPoint p3(50, 49);
        m.add(p1);
        m.add(p2);
        m.add(p3);
        QString xml;
        QTextStream str(&xml, QIODevice::WriteOnly);
        m.toXml(str);
        str.flush();

        QString expected =
            "<model id='6' name='' sampleRate='100' start='20' end='60' type='sparse' dimensions='2' resolution='10' notifyOnAdd='true' dataset='6' subtype='path' />\n"
            "<dataset id='6' dimensions='2'>\n"
            "  <point frame='20' mapframe='30' />\n"
            "  <point frame='40' mapframe='60' />\n"
            "  <point frame='50' mapframe='49' />\n"
            "</dataset>\n";
        expected.replace("\'", "\"");
        if (xml != expected) {
            cerr << "Obtained xml:\n" << xml
                 << "\nExpected:\n" << expected << std::endl;
        }
        QCOMPARE(xml, expected);
    }

    void image_xml() {
        ImageModel m(100, 10, false);
        Event p1(20, 30, 40, "a label"); // value + duration should not be saved
        m.add(p1.withURI("/path/to/thing.png").withLevel(0.8f));
        QString xml;
        QTextStream str(&xml, QIODevice::WriteOnly);
        m.toXml(str);
        str.flush();

        QString expected =
            "<model id='8' name='' sampleRate='100' start='20' end='30' type='sparse' dimensions='1' resolution='10' notifyOnAdd='true' dataset='7' subtype='image' />\n"
            "<dataset id='7' dimensions='1'>\n"
            "  <point frame='20' label='a label' image='/path/to/thing.png' />\n"
            "</dataset>\n";
        expected.replace("\'", "\"");
        if (xml != expected) {
            cerr << "Obtained xml:\n" << xml
                 << "\nExpected:\n" << expected << std::endl;
        }
        QCOMPARE(xml, expected);
    }
};

#endif