1 #include <gtest/gtest.h>
2 
3 #include <QDir>
4 #include <QUrl>
5 #include <QScopedPointer>
6 #include <QtDebug>
7 
8 #include "controllers/controller.h"
9 #include "controllers/controllerpresetfilehandler.h"
10 #include "controllers/controllerpresetinfoenumerator.h"
11 #include "controllers/midi/midicontrollerpreset.h"
12 #include "controllers/hid/hidcontrollerpreset.h"
13 #include "controllers/defs_controllers.h"
14 #include "test/mixxxtest.h"
15 
16 class FakeController : public Controller {
17   public:
18     explicit FakeController(UserSettingsPointer pConfig);
19     ~FakeController() override;
20 
presetExtension()21     QString presetExtension() override {
22         // Doesn't affect anything at the moment.
23         return ".test.xml";
24     }
25 
getPreset() const26     ControllerPresetPointer getPreset() const override {
27         if (m_bHidPreset) {
28             HidControllerPreset* pClone = new HidControllerPreset();
29             *pClone = m_hidPreset;
30             return ControllerPresetPointer(pClone);
31         } else {
32             MidiControllerPreset* pClone = new MidiControllerPreset();
33             *pClone = m_midiPreset;
34             return ControllerPresetPointer(pClone);
35         }
36     }
37 
visit(const MidiControllerPreset * preset)38     void visit(const MidiControllerPreset* preset) override {
39         m_bMidiPreset = true;
40         m_bHidPreset = false;
41         m_midiPreset = *preset;
42         m_hidPreset = HidControllerPreset();
43     }
visit(const HidControllerPreset * preset)44     void visit(const HidControllerPreset* preset) override {
45         m_bMidiPreset = false;
46         m_bHidPreset = true;
47         m_midiPreset = MidiControllerPreset();
48         m_hidPreset = *preset;
49     }
50 
accept(ControllerVisitor * visitor)51     void accept(ControllerVisitor* visitor) override {
52         // Do nothing since we aren't a normal controller.
53         Q_UNUSED(visitor);
54     }
55 
isMappable() const56     bool isMappable() const override {
57         if (m_bMidiPreset) {
58             return m_midiPreset.isMappable();
59         } else if (m_bHidPreset) {
60             return m_hidPreset.isMappable();
61         }
62         return false;
63     }
64 
matchPreset(const PresetInfo & preset)65     bool matchPreset(const PresetInfo& preset) override {
66         // We're not testing product info matching in this test.
67         Q_UNUSED(preset);
68         return false;
69     }
70 
71   protected:
send(const QList<int> & data,unsigned int length,unsigned int reportID)72     Q_INVOKABLE void send(const QList<int>& data, unsigned int length, unsigned int reportID) {
73         Q_UNUSED(data);
74         Q_UNUSED(length);
75         Q_UNUSED(reportID);
76     }
77 
78   private slots:
open()79     int open() override {
80         return 0;
81     }
close()82     int close() override {
83         return 0;
84     }
85 
86   private:
send(const QByteArray & data)87     void send(const QByteArray& data) override {
88         Q_UNUSED(data);
89     }
send(const QByteArray & data,unsigned int reportID)90     virtual void send(const QByteArray& data, unsigned int reportID) {
91         Q_UNUSED(data);
92         Q_UNUSED(reportID);
93     }
94 
preset()95     ControllerPreset* preset() override {
96         if (m_bHidPreset) {
97             return &m_hidPreset;
98         } else {
99             // Default to MIDI.
100             return &m_midiPreset;
101         }
102     }
103 
104     bool m_bMidiPreset;
105     bool m_bHidPreset;
106     MidiControllerPreset m_midiPreset;
107     HidControllerPreset m_hidPreset;
108 };
109 
FakeController(UserSettingsPointer pConfig)110 FakeController::FakeController(UserSettingsPointer pConfig)
111         : Controller(pConfig), m_bMidiPreset(false), m_bHidPreset(false) {
112 }
113 
~FakeController()114 FakeController::~FakeController() {
115 }
116 
117 class ControllerPresetValidationTest : public MixxxTest {
118   protected:
SetUp()119     void SetUp() override {
120         m_presetPath = QDir::current();
121         m_presetPath.cd("res/controllers");
122         m_pEnumerator.reset(new PresetInfoEnumerator(QList<QString>{m_presetPath.absolutePath()}));
123     }
124 
testLoadPreset(const PresetInfo & preset)125     bool testLoadPreset(const PresetInfo& preset) {
126         ControllerPresetPointer pPreset =
127                 ControllerPresetFileHandler::loadPreset(
128                         preset.getPath(),
129                         m_presetPath);
130         if (pPreset.isNull()) {
131             return false;
132         }
133 
134         FakeController controller(config());
135         controller.setDeviceName("Test Controller");
136         controller.startEngine();
137         controller.setPreset(*pPreset);
138         // Do not initialize the scripts.
139         bool result = controller.applyPreset(false);
140         controller.stopEngine();
141         return result;
142     }
143 
144     QDir m_presetPath;
145     QScopedPointer<PresetInfoEnumerator> m_pEnumerator;
146 };
147 
checkUrl(const QString & url)148 bool checkUrl(const QString& url) {
149     return QUrl(url).isValid();
150 }
151 
lintPresetInfo(const PresetInfo & preset)152 bool lintPresetInfo(const PresetInfo& preset) {
153     bool result = true;
154     if (preset.getName().trimmed().isEmpty()) {
155         qWarning() << "LINT:" << preset.getPath() << "has no name.";
156         result = false;
157     }
158 
159     if (preset.getAuthor().trimmed().isEmpty()) {
160         qWarning() << "LINT:" << preset.getPath() << "has no author.";
161     }
162 
163     if (preset.getDescription().trimmed().isEmpty()) {
164         qWarning() << "LINT:" << preset.getPath() << "has no description.";
165     }
166 
167     if (preset.getForumLink().trimmed().isEmpty()) {
168         qWarning() << "LINT:" << preset.getPath() << "has no forum link.";
169     } else if (!checkUrl(preset.getForumLink())) {
170         qWarning() << "LINT:" << preset.getPath() << "has invalid forum link";
171         result = false;
172     }
173 
174     if (preset.getWikiLink().trimmed().isEmpty()) {
175         qWarning() << "LINT:" << preset.getPath() << "has no wiki link.";
176     } else if (!checkUrl(preset.getWikiLink())) {
177         qWarning() << "LINT:" << preset.getPath() << "has invalid wiki link";
178         result = false;
179     }
180     return result;
181 }
182 
TEST_F(ControllerPresetValidationTest,MidiPresetsValid)183 TEST_F(ControllerPresetValidationTest, MidiPresetsValid) {
184     foreach (const PresetInfo& preset,
185              m_pEnumerator->getPresetsByExtension(MIDI_PRESET_EXTENSION)) {
186         qDebug() << "Validating" << preset.getPath();
187         EXPECT_TRUE(preset.isValid());
188         EXPECT_TRUE(lintPresetInfo(preset));
189         EXPECT_TRUE(testLoadPreset(preset));
190     }
191 }
192 
TEST_F(ControllerPresetValidationTest,HidPresetsValid)193 TEST_F(ControllerPresetValidationTest, HidPresetsValid) {
194     foreach (const PresetInfo& preset,
195              m_pEnumerator->getPresetsByExtension(HID_PRESET_EXTENSION)) {
196         qDebug() << "Validating" << preset.getPath();
197         EXPECT_TRUE(preset.isValid());
198         EXPECT_TRUE(lintPresetInfo(preset));
199         EXPECT_TRUE(testLoadPreset(preset));
200     }
201 }
202 
TEST_F(ControllerPresetValidationTest,BulkPresetsValid)203 TEST_F(ControllerPresetValidationTest, BulkPresetsValid) {
204     foreach (const PresetInfo& preset,
205              m_pEnumerator->getPresetsByExtension(BULK_PRESET_EXTENSION)) {
206         qDebug() << "Validating" << preset.getPath();
207         EXPECT_TRUE(preset.isValid());
208         EXPECT_TRUE(lintPresetInfo(preset));
209         EXPECT_TRUE(testLoadPreset(preset));
210     }
211 }
212