Chris@1611: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
Chris@1611: 
Chris@1611: /*
Chris@1611:     Sonic Visualiser
Chris@1611:     An audio file viewer and annotation editor.
Chris@1611:     Centre for Digital Music, Queen Mary, University of London.
Chris@1611:     
Chris@1611:     This program is free software; you can redistribute it and/or
Chris@1611:     modify it under the terms of the GNU General Public License as
Chris@1611:     published by the Free Software Foundation; either version 2 of the
Chris@1611:     License, or (at your option) any later version.  See the file
Chris@1611:     COPYING included with this distribution for more information.
Chris@1611: */
Chris@1611: 
Chris@1611: #ifndef TEST_SPARSE_MODELS_H
Chris@1611: #define TEST_SPARSE_MODELS_H
Chris@1611: 
Chris@1611: #include "../SparseOneDimensionalModel.h"
Chris@1611: #include "../NoteModel.h"
Chris@1661: #include "../TextModel.h"
Chris@1741: #include "../Path.h"
Chris@1674: #include "../ImageModel.h"
Chris@1611: 
Chris@1611: #include <QObject>
Chris@1611: #include <QtTest>
Chris@1611: 
Chris@1611: #include <iostream>
Chris@1611: 
Chris@1611: using namespace std;
Chris@1611: 
Chris@1677: // NB model & dataset IDs in the export tests are incremental,
Chris@1677: // depending on how many have been exported in previous tests - so
Chris@1677: // when adding or removing tests we may occasionally need to update
Chris@1677: // the IDs in other ones
Chris@1677: 
Chris@1611: class TestSparseModels : public QObject
Chris@1611: {
Chris@1611:     Q_OBJECT
Chris@1611: 
Chris@1611: private slots:
Chris@1611:     void s1d_empty() {
Chris@1611:         SparseOneDimensionalModel m(100, 10, false);
Chris@1611:         QCOMPARE(m.isEmpty(), true);
Chris@1658:         QCOMPARE(m.getEventCount(), 0);
Chris@1689:         QCOMPARE(m.getAllEvents().size(), size_t(0));
Chris@1717:         QCOMPARE(m.getStartFrame(), sv_frame_t(0));
Chris@1717:         QCOMPARE(m.getEndFrame(), sv_frame_t(0));
Chris@1689:         QCOMPARE(m.getSampleRate(), 100.0);
Chris@1611:         QCOMPARE(m.getResolution(), 10);
Chris@1611:         QCOMPARE(m.isSparse(), true);
Chris@1611: 
Chris@1658:         Event p(10);
Chris@1658:         m.add(p);
Chris@1658:         m.remove(p);
Chris@1611:         QCOMPARE(m.isEmpty(), true);
Chris@1658:         QCOMPARE(m.getEventCount(), 0);
Chris@1689:         QCOMPARE(m.getAllEvents().size(), size_t(0));
Chris@1717:         QCOMPARE(m.getStartFrame(), sv_frame_t(0));
Chris@1717:         QCOMPARE(m.getEndFrame(), sv_frame_t(0));
Chris@1611:     }
Chris@1611: 
Chris@1611:     void s1d_extents() {
Chris@1611:         SparseOneDimensionalModel m(100, 10, false);
Chris@1658:         Event p1(20);
Chris@1658:         m.add(p1);
Chris@1611:         QCOMPARE(m.isEmpty(), false);
Chris@1658:         QCOMPARE(m.getEventCount(), 1);
Chris@1658:         Event p2(50);
Chris@1658:         m.add(p2);
Chris@1611:         QCOMPARE(m.isEmpty(), false);
Chris@1658:         QCOMPARE(m.getEventCount(), 2);
Chris@1689:         QCOMPARE(m.getAllEvents().size(), size_t(2));
Chris@1658:         QCOMPARE(*m.getAllEvents().begin(), p1);
Chris@1658:         QCOMPARE(*m.getAllEvents().rbegin(), p2);
Chris@1717:         QCOMPARE(m.getStartFrame(), sv_frame_t(20));
Chris@1717:         QCOMPARE(m.getEndFrame(), sv_frame_t(60));
Chris@1658:         QCOMPARE(m.containsEvent(p1), true);
Chris@1658:         m.remove(p1);
Chris@1658:         QCOMPARE(m.getEventCount(), 1);
Chris@1689:         QCOMPARE(m.getAllEvents().size(), size_t(1));
Chris@1658:         QCOMPARE(*m.getAllEvents().begin(), p2);
Chris@1717:         QCOMPARE(m.getStartFrame(), sv_frame_t(50));
Chris@1717:         QCOMPARE(m.getEndFrame(), sv_frame_t(60));
Chris@1658:         QCOMPARE(m.containsEvent(p1), false);
Chris@1611:     }
Chris@1611:              
Chris@1611:     void s1d_sample() {
Chris@1611:         SparseOneDimensionalModel m(100, 10, false);
Chris@1658:         Event p1(20), p2(20), p3(50);
Chris@1658:         m.add(p1);
Chris@1658:         m.add(p2);
Chris@1658:         m.add(p3);
Chris@1689:         QCOMPARE(m.getAllEvents().size(), size_t(3));
Chris@1658:         QCOMPARE(*m.getAllEvents().begin(), p1);
Chris@1658:         QCOMPARE(*m.getAllEvents().rbegin(), p3);
Chris@1659: 
Chris@1659:         // The EventSeries that is used internally is tested more
Chris@1659:         // thoroughly in its own test suite. This is just a check
Chris@1659:         auto pp = m.getEventsWithin(20, 10);
Chris@1689:         QCOMPARE(pp.size(), size_t(2));
Chris@1611:         QCOMPARE(*pp.begin(), p1);
Chris@1611:         QCOMPARE(*pp.rbegin(), p2);
Chris@1643:         
Chris@1659:         pp = m.getEventsWithin(40, 10);
Chris@1689:         QCOMPARE(pp.size(), size_t(0));
Chris@1611: 
Chris@1659:         pp = m.getEventsStartingAt(50);
Chris@1689:         QCOMPARE(pp.size(), size_t(1));
Chris@1611:         QCOMPARE(*pp.begin(), p3);
Chris@1611:     }
Chris@1611: 
Chris@1611:     void s1d_xml() {
Chris@1611:         SparseOneDimensionalModel m(100, 10, false);
Chris@1611:         m.setObjectName("This \"&\" that");
Chris@1674:         Event p1(20);
Chris@1674:         Event p2(20, "Label &'\">");
Chris@1674:         Event p3(50, 12.4f, 16, ""); // value + duration should not be saved
Chris@1658:         m.add(p1);
Chris@1658:         m.add(p2);
Chris@1658:         m.add(p3);
Chris@1611:         QString xml;
Chris@1611:         QTextStream str(&xml, QIODevice::WriteOnly);
Chris@1611:         m.toXml(str);
Chris@1611:         str.flush();
Chris@1611:         QString expected =
Chris@1685:             "<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"
Chris@1685:             "<dataset id='0' dimensions='1'>\n"
Chris@1685:             "  <point frame='20' label='' />\n"
Chris@1611:             "  <point frame='20' label='Label &amp;&apos;&quot;&gt;' />\n"
Chris@1685:             "  <point frame='50' label='' />\n"
Chris@1611:             "</dataset>\n";
Chris@1611:         expected.replace("\'", "\"");
Chris@1611:         if (xml != expected) {
Chris@1611:             cerr << "Obtained xml:\n" << xml
Chris@1697:                  << "\nExpected:\n" << expected << std::endl;
Chris@1611:         }
Chris@1611:         QCOMPARE(xml, expected);
Chris@1611:     }
Chris@1611: 
Chris@1611:     void note_extents() {
Chris@1611:         NoteModel m(100, 10, false);
Chris@1643:         Event p1(20, 123.4f, 40, 0.8f, "note 1");
Chris@1644:         m.add(p1);
Chris@1611:         QCOMPARE(m.isEmpty(), false);
Chris@1644:         QCOMPARE(m.getEventCount(), 1);
Chris@1643:         Event p2(50, 124.3f, 30, 0.9f, "note 2");
Chris@1644:         m.add(p2);
Chris@1611:         QCOMPARE(m.isEmpty(), false);
Chris@1644:         QCOMPARE(m.getEventCount(), 2);
Chris@1689:         QCOMPARE(m.getAllEvents().size(), size_t(2));
Chris@1644:         QCOMPARE(*m.getAllEvents().begin(), p1);
Chris@1644:         QCOMPARE(*m.getAllEvents().rbegin(), p2);
Chris@1717:         QCOMPARE(m.getStartFrame(), sv_frame_t(20));
Chris@1717:         QCOMPARE(m.getEndFrame(), sv_frame_t(80));
Chris@1644:         QCOMPARE(m.containsEvent(p1), true);
Chris@1643:         QCOMPARE(m.getValueMinimum(), 123.4f);
Chris@1643:         QCOMPARE(m.getValueMaximum(), 124.3f);
Chris@1644:         m.remove(p1);
Chris@1644:         QCOMPARE(m.getEventCount(), 1);
Chris@1689:         QCOMPARE(m.getAllEvents().size(), size_t(1));
Chris@1644:         QCOMPARE(*m.getAllEvents().begin(), p2);
Chris@1717:         QCOMPARE(m.getStartFrame(), sv_frame_t(50));
Chris@1717:         QCOMPARE(m.getEndFrame(), sv_frame_t(80));
Chris@1644:         QCOMPARE(m.containsEvent(p1), false);
Chris@1611:     }
Chris@1611:              
Chris@1611:     void note_sample() {
Chris@1611:         NoteModel m(100, 10, false);
Chris@1643:         Event p1(20, 123.4f, 10, 0.8f, "note 1");
Chris@1643:         Event p2(20, 124.3f, 20, 0.9f, "note 2");
Chris@1643:         Event p3(50, 126.3f, 30, 0.9f, "note 3");
Chris@1644:         m.add(p1);
Chris@1644:         m.add(p2);
Chris@1644:         m.add(p3);
Chris@1611: 
Chris@1689:         QCOMPARE(m.getAllEvents().size(), size_t(3));
Chris@1644:         QCOMPARE(*m.getAllEvents().begin(), p1);
Chris@1644:         QCOMPARE(*m.getAllEvents().rbegin(), p3);
Chris@1611: 
Chris@1644:         auto pp = m.getEventsSpanning(20, 10);
Chris@1689:         QCOMPARE(pp.size(), size_t(2));
Chris@1611:         QCOMPARE(*pp.begin(), p1);
Chris@1611:         QCOMPARE(*pp.rbegin(), p2);
Chris@1611: 
Chris@1644:         pp = m.getEventsSpanning(30, 20);
Chris@1689:         QCOMPARE(pp.size(), size_t(1));
Chris@1643:         QCOMPARE(*pp.begin(), p2);
Chris@1611: 
Chris@1644:         pp = m.getEventsSpanning(40, 10);
Chris@1689:         QCOMPARE(pp.size(), size_t(0));
Chris@1611: 
Chris@1644:         pp = m.getEventsCovering(50);
Chris@1689:         QCOMPARE(pp.size(), size_t(1));
Chris@1644:         QCOMPARE(*pp.begin(), p3);
Chris@1611:     }
Chris@1611: 
Chris@1611:     void note_xml() {
Chris@1611:         NoteModel m(100, 10, false);
Chris@1643:         Event p1(20, 123.4f, 20, 0.8f, "note 1");
Chris@1643:         Event p2(20, 124.3f, 10, 0.9f, "note 2");
Chris@1643:         Event p3(50, 126.3f, 30, 0.9f, "note 3");
Chris@1611:         m.setScaleUnits("Hz");
Chris@1644:         m.add(p1);
Chris@1644:         m.add(p2);
Chris@1644:         m.add(p3);
Chris@1611:         QString xml;
Chris@1611:         QTextStream str(&xml, QIODevice::WriteOnly);
Chris@1611:         m.toXml(str);
Chris@1611:         str.flush();
Chris@1651:         
Chris@1611:         QString expected =
Chris@1685:             "<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"
Chris@1685:             "<dataset id='2' dimensions='3'>\n"
Chris@1643:             "  <point frame='20' value='124.3' duration='10' level='0.9' label='note 2' />\n"
Chris@1611:             "  <point frame='20' value='123.4' duration='20' level='0.8' label='note 1' />\n"
Chris@1611:             "  <point frame='50' value='126.3' duration='30' level='0.9' label='note 3' />\n"
Chris@1611:             "</dataset>\n";
Chris@1611:         expected.replace("\'", "\"");
Chris@1611:         if (xml != expected) {
Chris@1611:             cerr << "Obtained xml:\n" << xml
Chris@1697:                  << "\nExpected:\n" << expected << std::endl;
Chris@1611:         }
Chris@1611:         QCOMPARE(xml, expected);
Chris@1611:     }
Chris@1661: 
Chris@1661:     void text_xml() {
Chris@1661:         TextModel m(100, 10, false);
Chris@1661:         Event p1(20, 1.0f, "text 1");
Chris@1661:         Event p2(20, 0.0f, "text 2");
Chris@1661:         Event p3(50, 0.3f, "text 3");
Chris@1661:         m.add(p1);
Chris@1674:         m.add(p2.withLevel(0.8f));
Chris@1661:         m.add(p3);
Chris@1661:         QString xml;
Chris@1661:         QTextStream str(&xml, QIODevice::WriteOnly);
Chris@1661:         m.toXml(str);
Chris@1661:         str.flush();
Chris@1661: 
Chris@1661:         QString expected =
Chris@1685:             "<model id='5' name='' sampleRate='100' start='20' end='60' type='sparse' dimensions='2' resolution='10' notifyOnAdd='true' dataset='4' subtype='text' />\n"
Chris@1685:             "<dataset id='4' dimensions='2'>\n"
Chris@1661:             "  <point frame='20' height='0' label='text 2' />\n"
Chris@1661:             "  <point frame='20' height='1' label='text 1' />\n"
Chris@1661:             "  <point frame='50' height='0.3' label='text 3' />\n"
Chris@1661:             "</dataset>\n";
Chris@1661:         expected.replace("\'", "\"");
Chris@1661:         if (xml != expected) {
Chris@1661:             cerr << "Obtained xml:\n" << xml
Chris@1697:                  << "\nExpected:\n" << expected << std::endl;
Chris@1661:         }
Chris@1661:         QCOMPARE(xml, expected);
Chris@1661:     }
Chris@1662:     
Chris@1662:     void path_xml() {
Chris@1741:         Path m(100, 10);
Chris@1662:         PathPoint p1(20, 30);
Chris@1662:         PathPoint p2(40, 60);
Chris@1662:         PathPoint p3(50, 49);
Chris@1663:         m.add(p1);
Chris@1663:         m.add(p2);
Chris@1663:         m.add(p3);
Chris@1662:         QString xml;
Chris@1662:         QTextStream str(&xml, QIODevice::WriteOnly);
Chris@1662:         m.toXml(str);
Chris@1662:         str.flush();
Chris@1662: 
Chris@1662:         QString expected =
Chris@1685:             "<model id='6' name='' sampleRate='100' start='20' end='60' type='sparse' dimensions='2' resolution='10' notifyOnAdd='true' dataset='6' subtype='path' />\n"
Chris@1685:             "<dataset id='6' dimensions='2'>\n"
Chris@1662:             "  <point frame='20' mapframe='30' />\n"
Chris@1662:             "  <point frame='40' mapframe='60' />\n"
Chris@1662:             "  <point frame='50' mapframe='49' />\n"
Chris@1662:             "</dataset>\n";
Chris@1662:         expected.replace("\'", "\"");
Chris@1662:         if (xml != expected) {
Chris@1662:             cerr << "Obtained xml:\n" << xml
Chris@1697:                  << "\nExpected:\n" << expected << std::endl;
Chris@1662:         }
Chris@1662:         QCOMPARE(xml, expected);
Chris@1662:     }
Chris@1663: 
Chris@1674:     void image_xml() {
Chris@1674:         ImageModel m(100, 10, false);
Chris@1674:         Event p1(20, 30, 40, "a label"); // value + duration should not be saved
Chris@1674:         m.add(p1.withURI("/path/to/thing.png").withLevel(0.8f));
Chris@1674:         QString xml;
Chris@1674:         QTextStream str(&xml, QIODevice::WriteOnly);
Chris@1674:         m.toXml(str);
Chris@1674:         str.flush();
Chris@1674: 
Chris@1674:         QString expected =
Chris@1685:             "<model id='8' name='' sampleRate='100' start='20' end='30' type='sparse' dimensions='1' resolution='10' notifyOnAdd='true' dataset='7' subtype='image' />\n"
Chris@1685:             "<dataset id='7' dimensions='1'>\n"
Chris@1674:             "  <point frame='20' label='a label' image='/path/to/thing.png' />\n"
Chris@1674:             "</dataset>\n";
Chris@1674:         expected.replace("\'", "\"");
Chris@1674:         if (xml != expected) {
Chris@1674:             cerr << "Obtained xml:\n" << xml
Chris@1697:                  << "\nExpected:\n" << expected << std::endl;
Chris@1674:         }
Chris@1674:         QCOMPARE(xml, expected);
Chris@1674:     }
Chris@1611: };
Chris@1611: 
Chris@1611: #endif